Enviando vários arquivos sem exibir input file

Dia 06/07/2007 na lista Webstandards-br foi questionado como o gmail fazia, para que sem o input file, fosse enviado arquivo. Porém isso só acontecia no IE, no Firefox era um input normal.O pessoal deu uma olhada no código e concluiu que no IE funcionava algo como:

document.getElementById('InputFile').click();

Porém no Firefox isso não funciona. Não querendo entrar no mérito de qual navegador está certo, pensei em um modo de driblar isso e o resultando acabou resultando neste artigo.

Como fazer

Minha idéia para contornar isso é utilizar um input que fique transparente e colocar uma imagem no fundo do elemento onde esse input está! Veja que não podemos esconder o input, pois ele precisa ser clicado, mas sem que o usuário perceba. Então vamos ao HTML básico:

<form method="post" enctype="multipart/form-data" id="UploadForm">
  <p id="uploadButton"> <input id="inputFile" name="inputFile" size="1" type="file" /></p>
</form>

Temos um div chamado uploadButton, que conterá a imagem de fundo e o input que fará a coisa acontecer.

Adicionamos um pouco de CSS:

#inputFile {
  float: right;
  opacity: 0;
  filter: alpha(opacity=0);
}
#uploadButton {
  width:88px;
  height:20px;
  background:url("adicionar.jpg") no-repeat;
  float:left;
  margin: 0 20px 0 0;
}
#files {
  clear:both;
}

Veja que no #inputFile definimos opacity, para funciona no Firefox e um filter para ficar transparente no IE. O #files veremos pra que serve daqui a pouco.

Fazendo a mágica acontecer com javascript

Utilizei a jQuery por pura comodidade, mas poderia facilmente ser feito na mão. Na verdade para enviar um arquivo precisariamos do javascript apenas para exibir o nome do arquivo em algum lugar. Mas como queremos fazer o upload de vários arquivos:

var InputCount = 1;
$(function() {
  $("#UploadForm").append("<ul id='files'></ul>");
  $("#inputFile").change(function () { newFile() });
});
function newFile() {
  $("#files").append("&lt;li&gt;" + $("#inputFile").val() + "&lt;/li&gt;");
  $("#inputFile").hide();
  $("#inputFile").clone().appendTo("#uploadButton");
  $("#inputFile").attr("id", "inputFile" + InputCount);
  $("#inputFile" + InputCount).attr("name", "inputFile" + InputCount);
  InputCount++;
  $("#inputFile").change( function() { newFile(); } );
  $("#inputFile").val("");
  $("#inputFile").show();
}

Para finalizar ainda seria necessário adicionar um botão de submit e tratar o retorno no servidor. Porém isso não será tratado aqui!

Após escrever todo o exemplo e o artigo, resolvi testar no IE6 (o que devia ter sido feito antes) e percebi que não funcionava o filtro da opacidade, apesar de tudo dar certo no IE 5 e no IE 7.