fakeFile: como estilizar um input type=file

Pois é, essa era uma questão incômoda para mim até algum tempo atrás. É impossível! inputs com type=file não aceitam quase nenhuma regra CSS e, a não ser que estejamos trabalhando com formulários sem formatação, ficam sempre destoando, "quebrando" o layout do formulário.

Então, na busca de soluções possíveis, encontrei algumas baseadas nessa lógica, que pareceu ser a única viável e que consiste em tornar o input invisível com a propriedade opacity e e então colocá-lo por cima de uma imagem, que deve simular o elemento. Bacana, mas como cada browser renderiza o input de um jeito diferente (tamanhos do campo de texto e do botão), dificilmente a nossa imagem correspoderia ao tamanho do botão, o que provocaria confusões quanto ao local do clique.

Pensei então em acrescentar alguns detalhes que fizessem o input se adaptar à imagem ao invés de tentar fazer uma imagem que casasse com as proporções do input (que são variáveis). A idéia era fazer com que o botão do input ficasse num tamanho que cubrisse totalmente a imagem, mas dando um jeito de esconder a parte do input que excedeu a imagem.

Primeiro problema: posso definir altura e largura, mas como o texto no botão é sempre igual (Procurar...) eu conseguia apenas uma mudança disso:

Para isso:

Se temos uma imagem maior como esta:

certamente parte dela ficará insensível ao clique, pois a largura do botão continua a mesma - e só ele é sensível ao clique. Então, para mudar a largura do botão, podemos aumentar o tamanho da fonte! Ok, mas para que tamanho? um tamanho fixo não parecia muito legal... cheguei então à uma proporção que se mostrou correta nos meus testes: (largura da imagem / 5).

E ao aumentar o tamanho da fonte, consigo algo assim:

Isso aumenta a largura do botão. Ok, agora precisamos colocar o input sobre a imagem, mas alinhado à direita para tirar a parte texto do input file de cima da nossa imagem. Aí resolvi criar uma função Javascript para fazer todo o necessário de uma vez. A lógica que usei foi a seguinte:

  • Leio o tamanho da imagem
  • Crio um DIV exatamente do mesmo tamanho e com style="position:relative; overlow:hidden;"
  • Insiro o DIV imediatamente antes da imagem
  • Coloco a imagem dentro do DIV
  • Calculo o tamanho da fonte para que a largura do botão cubra a imagem (FS = width/5 ( +/-... ))
  • Defino a propriedade style do input para "position:absolute; top:0; right:0; opacity:0; font-size:FSpx"
  • Coloco o input dentro do DIV

Bem, a função fakeFile recebe 3 parâmetros, sendo 2 obrigatórios: a imagem e o input file (como DOM objects) e um terceiro opcional que recebe um input file para receber o caminho para o arquivo a cada mudança no input file original. As funções addEvent e $ são utilizadas por fakeFile, por isso foram também incluídas

  1. function fakeFile(image, input, pathfield) {
  2. var w = image.offsetWidth, h = image.offsetHeight,
  3.     s = parseInt(w/4.2), wrapper = document.createElement('div');
  4. wrapper.style.cssText = "position:relative; width:"+w+"px; height:"+h+"px; overflow:hidden; z-index:100;";
  5. input.style.cssText = "position:absolute; height:"+h+"px; top:0; right:0; font-size:"+s+"px; "+
  6.                       "filter:alpha(opacity=0); opacity:0; z-index:101;";
  7.   if(pathfield)
  8.     addEvent(input, 'change', new Function('$("'+pathfield.id+'").value = $("'+input.id+'").value;'));
  9. image.parentNode.insertBefore(wrapper, image);
  10. wrapper.appendChild(image);
  11. wrapper.appendChild(input);
  12. return wrapper;
  13. }
  14.  
  15. function $(id) { return typeof(id) == 'object' ? id : (document.getElementById(id) || null); }
  16.  
  17. function addEvent(obj, evType, fn){
  18.  if(obj.addEventListener) obj.addEventListener(evType, fn, false);
  19.  else if(obj.attachEvent) obj.attachEvent("on"+evType, fn);
  20. }

Veja aqui uma página de exemplo.

Categoria:Funções
Tags: ,
Permalink | Faça um comentário ou um trackback.

4 Comentários

  1. Hiran
    Em 1 de maio de 2009 às 23:00 | Permalink

    td bem…
    fiz esse código que abre varios input files (quastos por preciso), vi o que vc fez para trocar o buuton por imagen, e já usei , e foi legal, porem agora preciso personalizar o button nesse script.
    se puder me ajudar seria legal… não sei mais a quem recorrer…
    segui ai o codigo:

    var upload_number = 2;
    function addFileInput() {
    var d = document.createElement(“div”);
    var file = document.createElement(“input”);
    file.setAttribute(“type”, “file”);
    file.setAttribute(“name”, “attachment”+upload_number);
    file.setAttribute(“size”, “40″);

    d.appendChild(file);
    document.getElementById(“moreUploads”).appendChild(d);
    upload_number++;
    }

    Adicionar outro arquivo

  2. Em 4 de maio de 2009 às 23:58 | Permalink

    Fala cara, desculpe a demora… este blog anda bem esquecido (isso vai mudar!)
    Bem o que você quer é simplesmente repetir a mesma coisa feita no primeiro input para os outros. Mas temos q ter uma imagem para cada, portanto, creio q seria melhor criá-la on-the-fly também

    var upload_number = 2;
    function addFileInput() {
    var d = document.createElement(”div”);
    var file = document.createElement(”input”);
    file.setAttribute(”type”, “file”);
    file.setAttribute(”name”, “attachment”+upload_number);
    file.setAttribute(”size”, “40″);
    d.appendChild(file);
    document.getElementById(”moreUploads”).appendChild(d);
    // mais ou menos isso:
    var image = document.createElement(”image”);
    image.src = ‘…’;
    document.getElementById(”moreUploads”).appendChild(image);
    fakeFile(image, file);

    upload_number++;
    }

  3. Hiran
    Em 5 de maio de 2009 às 1:30 | Permalink

    Cara…
    Valeu pela ajuda… acabei fazendo uma adaptação ao escript uqe vc postou e acabei criando um lance bacana para o input file… condegui personalizar e deicar meio com a cara do gmail…. sem o campo onde de texto.. só o botão… logo logo vou colocar um site no ar e ai te mando o endereço pra vc ver como ficou…
    Mas tô com outra dificuldade, tentei usar aquele exemplo que vc criou que “Mostra uma mensagem quando input está vazio”, o problema eh quando o campi eh input password, a mensagem fica com as “bolinhas” como quando digitamos a senha… como posso fazer pra aparecer a mensagem quando o campo senha esta vazio e as “bolinhas” quando digitada a senha.???

  4. Daniel Malaquias
    Em 13 de janeiro de 2011 às 16:33 | Permalink

    Acrescentei estas suas linhas
    wrapper.style.cssText = “position:relative; width:”+w+”px; height:”+h+”px; overflow:hidden; z-index:100; cursor:hand;”;
    input.style.cssText = “position:absolute; height:”+h+”px; top:0; right:0; font-size:”+s+”px; “+
    “filter:alpha(opacity=0); opacity:0; z-index:101; cursor:hand;”;
    assim aparece no IE o cursor tipo hand para o usuário.

Faça um comentário

Seu email nunca mostrado ou compartilhado. Os campos com * são obrigatórios

*
*