Em JavaScript, posso disparar um evento de "clique" programaticamente para um elemento de entrada de arquivo?

261

Eu gostaria de fazer um evento de clique disparar em uma <input type="file">tag programaticamente.

Apenas chamar click () não parece fazer nada ou, pelo menos, não abre uma caixa de diálogo de seleção de arquivo.

Venho experimentando capturar eventos usando ouvintes e redirecionando o evento, mas não consegui fazer isso para realmente executar o evento como se alguém tivesse clicado nele.

user28655
fonte
1
O evento de clique que você acionar deve ser chamado dentro de um evento de clique iniciado pelo usuário ou não funcionará.
Umagon

Respostas:

79

Você não pode fazer isso em todos os navegadores, supostamente o IE faz permitem, mas Mozilla e Opera não.

Quando você redige uma mensagem no GMail, o recurso 'anexar arquivos' é implementado de uma maneira para o IE e qualquer navegador que ofereça suporte a isso e, em seguida, implementa outra maneira para o Firefox e os navegadores que não.

Não sei por que você não pode fazer isso, mas uma coisa que é um risco à segurança e que você não tem permissão para fazer em nenhum navegador é definir programaticamente o nome do arquivo no elemento Arquivo HTML.

Jason Bunting
fonte
1
Drat. Bem, eu certamente entendo que é explorável. Isso está documentado em algum lugar? Eu acho que seria implementado por cada navegador?
user28655
Atualizei minha resposta para ser mais correta que a resposta anterior - a essência é a mesma, mas os esclarecimentos devem ajudar um pouco. Esse cara teve o mesmo problema: bytes.com/forum/thread542877.html
Jason Bunting
Graças a Deus que o FF5 permite esse clique
rshimoda
2
Para esclarecer o comentário acima: O Chrome foi alterado recentemente para verificar se o inputelemento do arquivo está visível. Disparar o clickmétodo funciona, inclusive no console, se o elemento puder ser visto.
jwadsack
2
@ Otvazhnii - cara, essa resposta (a que você está dizendo está errada) tem 10 anos - não é surpresa que não esteja correta! (Não sei ao certo, estou acreditando na sua palavra). : P
Jason Bunting
257

Eu tenho procurado a solução para este dia inteiro. E estas são as conclusões que tirei:

  1. Por razões de segurança, o Opera e o Firefox não permitem acionar a entrada de arquivos.
  2. A única alternativa conveniente é criar uma entrada de arquivo "oculto" (usando opacidade, não "oculto" ou "display: none"!) E depois criar o botão "abaixo" dele. Dessa maneira, o botão é visto, mas, ao clicar no usuário, ele realmente ativa a entrada do arquivo.

Espero que isto ajude! :)

<div style="display: block; width: 100px; height: 20px; overflow: hidden;">
<button style="width: 110px; height: 30px; position: relative; top: -5px; left: -5px;"><a href="javascript: void(0)">Upload File</a></button>
<input type="file" id="upload_input" name="upload" style="font-size: 50px; width: 120px; opacity: 0; filter:alpha(opacity=0);  position: relative; top: -40px;; left: -20px" />
</div>
Romas
fonte
6
Esta solução funciona muito bem. Não sei por que isso foi ignorado e não foi aprimorado. Não é exatamente o que a pergunta pede, mas é um ótimo trabalho. Você achou incompatível com qualquer navegador? Não tenho tempo para trabalhar nos 10 sabores mais relevantes para testar.
Yevgeny Simkin
1
Obrigado por esta resposta, é incrível: D
mario.tco 20/07/11
Ótima solução. Também funciona no navegador móvel Android.
Gstroup #
1
Isso não é verdade, veja a resposta de Didier abaixo. O clique programático deve vir do contexto de ação do usuário - como no manipulador de cliques de outro botão. Então funciona bem, não há necessidade de ter o elemento de estouro.
smok
1
O único problema com isso é que, se você deseja alterar o estilo do botão ao passar o mouse, não pode. O que significa que se você deseja que ele se pareça com todos os outros botões do seu aplicativo, não pode.
Vincent
68

Você pode acionar click () em qualquer navegador, mas alguns navegadores precisam que o elemento seja visível e focado. Aqui está um exemplo de jQuery:

$('#input_element').show();
$('#input_element').focus();
$('#input_element').click();
$('#input_element').hide();

Ele funciona com o hide antes do, click()mas não sei se funciona sem chamar o método show. Nunca tentei isso no Opera, testei no IE / FF / Safari / Chrome e funciona. Espero que isso ajude.

Florin Mogos
fonte
51
Obrigado por compartilhar. Apenas no caso de você não sabia - você pode usar o encadeamento em jQuery: $(...).show().focus().click().hide();:)
pimvdb
1
@pimvdb: tanto quanto eu testei, sua solução só funciona no Chrome.
Hoàng Long
1
@ Hoàng Long: Você quer dizer a solução de encadeamento ou Florin Mogos? Não acredito que o encadeamento faça diferenças entre navegadores.
Pimvdb 10/10
1
@ HoàngLong Funciona para mim no IE 8 e 9, nos mais recentes Chrome, Safari e Firefox.
Sami Samhuri 29/11
2
Estranho. Como eu testei, ele não funciona no Chrome no Ubuntu.
Sandrew
29

ISTO É POSSÍVEL: em FF4 +, Opera?, Chrome: mas:

  1. inputElement.click()deve ser chamado do contexto de ação do usuário! (não contexto de execução de script)

  2. <input type="file" />deve estar visível ( inputElement.style.display !== 'none') (você pode ocultá-lo com visibilidade ou outra coisa, mas não a propriedade "display")

Didier Ghys
fonte
Isso resolveu para mim. Eu tive que adicionar o javascript ao onclickatributo em vez de vincular ao evento.
jasonlfunk
1
Voto positivo a única solução razoável. O método de estouro é feio.
smok 21/03
Ha! Eu sabia que tinha que ter algo a ver com o contexto! Eu tinha observado que a chamada inputElement.click()de dentro de um evento de pressionamento de tecla (atalho para anexar um arquivo) funcionou, mas a chamada em um tempo limite ou retorno de chamada ajax NÃO. Votado.
precisa saber é o seguinte
9
Btw alguém tem mais recursos no "contexto de ação do usuário" versus "contexto de execução de script"? Tudo o que estou vendo ao pesquisar tem a ver com o contexto de execução e this. : /
brettjonesdev 14/04
@bretjonesdev Acho que isso significa que ele precisa ser executado dentro de um manipulador de eventos iniciado pelo usuário, como um manipulador de eventos de clique, versus uma promessa, tempo limite ou qualquer outro evento não iniciado pelo usuário.
Alex Guerra
10

Para aqueles que entendem que você precisa sobrepor um formulário invisível sobre o link, mas têm preguiça de escrever, eu escrevi para você. Bem, para mim, mas também pode compartilhar. Comentários são bem-vindos.

HTML (em algum lugar):

<a id="fileLink" href="javascript:fileBrowse();" onmouseover="fileMove();">File Browse</a>

HTML (em algum lugar em que você não se importa):

<div id="uploadForm" style="filter:alpha(opacity=0); opacity: 0.0; width: 300px; cursor: pointer;">
    <form method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
    </form>
</div>

JavaScript:

function pageY(el) {
    var ot = 0;
    while (el && el.offsetParent != el) {
        ot += el.offsetTop ? el.offsetTop : 0;
        el = el.offsetParent;
    }
    return ot;
}

function pageX(el) {
    var ol = 0;
    while (el && el.offsetParent != el) {
        ol += el.offsetLeft ? el.offsetLeft : 0;
        el = el.offsetParent;
    }
    return ol;
}

function fileMove() {
    if (navigator.appName == "Microsoft Internet Explorer") {
        return; // Don't need to do this in IE. 
    }
    var link = document.getElementById("fileLink");
    var form = document.getElementById("uploadForm");
    var x = pageX(link);
    var y = pageY(link);
    form.style.position = 'absolute';
    form.style.left = x + 'px';
    form.style.top = y + 'px';
}

function fileBrowse() {
    // This works in IE only. Doesn't do jack in FF. :( 
    var browseField = document.getElementById("uploadForm").file;
    browseField.click();
}
McTrafik
fonte
7

Se você deseja que o clickmétodo funcione no Chrome, Firefox, etc, aplique o seguinte estilo ao seu arquivo de entrada. Será perfeitamente escondido, é como se você fizesse umadisplay: none;

#fileInput {
    visibility: hidden;
    position: absolute;
    top: 0;
    left: -5000px;
}

É simples assim, eu testei que funciona!

Linblow
fonte
Eu gosto desta maneira da melhor maneira, pois alguns navegadores mais antigos não farão nada em inputElement.click () se estiver oculto.
Nick
Por que não apenas definir o heighte widthpara 0?
Solomon Ucko
5
$(document).one('mousemove', function() { $(element).trigger('click') } );

Funcionou para mim quando tive um problema semelhante, é um eRube Goldberg regular.

Leon Creatini
fonte
4

SOLUÇÃO DE TRABALHO

Deixe-me adicionar a este post antigo, uma solução funcional que eu costumava usar que funciona provavelmente em 80% ou mais de todos os navegadores, novos e antigos.

A solução é complexa, porém simples. A primeira etapa é usar CSS e disfarçar o tipo de arquivo de entrada com "subelementos" que são mostrados como opacos por 0. A próxima etapa é usar JavaScript para atualizar seu rótulo conforme necessário.

HTML Os IDs são simplesmente inseridos se você quiser uma maneira rápida de acessar um elemento específico, no entanto, as classes são obrigatórias, pois estão relacionadas ao CSS que define todo esse processo.

<div class="file-input wrapper">
    <input id="inpFile0" type="file" class="file-input control" />
    <div class="file-input content">
        <label id="inpFileOutput0" for="inpFileButton" class="file-input output">Click Here</label>
        <input id="inpFileButton0" type="button" class="file-input button" value="Select File" />
    </div>
</div>

CSS Lembre-se de que cores e estilos de fonte são totalmente sua preferência. Se você usar esse CSS básico, sempre poderá usar a marcação pós-final como quiser, isso é mostrado no jsFiddle listado no final.

.file-test-area {
    border: 1px solid;
    margin: .5em;
    padding: 1em;
}
.file-input {
    cursor: pointer !important;
}
.file-input * {
    cursor: pointer !important;
    display: inline-block;
}
.file-input.wrapper {
    display: inline-block;
    font-size: 14px;
    height: auto;
    overflow: hidden;
    position: relative;
    width: auto;
}
.file-input.control {
    -moz-opacity:0 ;
    filter:alpha(opacity: 0);
    opacity: 0;

    height: 100%;
    position: absolute;
    text-align: right;
    width: 100%;
    z-index: 2;
}
.file-input.content {
    position: relative;
    top: 0px;
    left: 0px;
    z-index: 1;
}
.file-input.output {
    background-color: #FFC;
    font-size: .8em;
    padding: .2em .2em .2em .4em;
    text-align: center;
    width: 10em;
}
.file-input.button {
    border: none;
    font-weight: bold;
    margin-left: .25em;
    padding: 0 .25em;
}

JavaScript Puro e verdadeiro, no entanto, alguns navegadores MAIS ANTIGOS (aposentados) ainda podem ter problemas com ele (como o Netscrape 2!)

var inp = document.getElementsByTagName('input');
for (var i=0;i<inp.length;i++) {
    if (inp[i].type != 'file') continue;
    inp[i].relatedElement = inp[i].parentNode.getElementsByTagName('label')[0];
    inp[i].onchange /*= inp[i].onmouseout*/ = function () {
        this.relatedElement.innerHTML = this.value;
    };
};

Exemplo de jsFiddle de trabalho

SpYk3HH
fonte
4

Funciona :

Por motivos de segurança no Firefox e Opera, você não pode acionar o clique na entrada do arquivo, mas pode simular com o MouseEvents:

<script>
click=function(element){
    if(element!=null){
        try {element.click();}
        catch(e) {
            var evt = document.createEvent("MouseEvents");
            evt.initMouseEvent("click",true,true,window,0,0,0,0,0,false,false,false,false,0,null);
            element.dispatchEvent(evt);
            }
        }
    };
</script>

<input type="button" value="upload" onclick="click(document.getElementById('inputFile'));"><input type="file" id="inputFile" style="display:none">
kop
fonte
1
Observe que o createEvent()e initMouseEvent()agora estão obsoletos.
Alexis Wilke
4

Eu sei que isso é antigo, e todas essas soluções são hacks em torno das precauções de segurança do navegador com valor real.

Dito isto, a partir de hoje, fileInput.click () funciona no Chrome atual (36.0.1985.125 m) e no Firefox ESR atual (24.7.0), mas não no IE atual (11.0.9600.17207). A sobreposição de um campo de arquivo com opacidade 0 em cima de um botão funciona, mas eu queria um elemento de link como acionador visível, e o sublinhado ao passar o mouse não funciona muito bem em nenhum navegador. Ele pisca e desaparece, provavelmente o navegador pensando se o estilo de foco flutuante realmente se aplica ou não.

Mas eu encontrei uma solução que funciona em todos esses navegadores. Não afirmo ter testado todas as versões de todos os navegadores, ou sei que continuará funcionando para sempre, mas parece atender às minhas necessidades agora.

É simples: posicione o campo de entrada do arquivo fora da tela (position: absolute; top: -5000px), coloque um elemento de rótulo em torno dele e acione o clique no rótulo, em vez do próprio campo do arquivo.

Observe que o link precisa ser script para chamar o método click do rótulo; ele não faz isso automaticamente, como quando você clica no texto dentro de um elemento do rótulo. Aparentemente, o elemento do link captura o clique e não chega ao rótulo.

Observe também que isso não fornece uma maneira de mostrar o arquivo selecionado no momento, pois o campo está fora da tela. Eu queria enviar imediatamente quando um arquivo foi selecionado, então isso não é um problema para mim, mas você precisará de uma abordagem um pouco diferente se sua situação for diferente.

enigment
fonte
Ok, na tag do botão, onclick = "filetag.click ()" não funciona com o IE 9 e 10 (mas funciona com o IE 11, Firefox 10/10/26/27/28, Chrome / Chromium 31/32/33 / 36, Safari 7, Opera 23). Mas, se você usar um rótulo para = "id-of-file-input" (sem onlick), ele funcionará para o IE 10/09/11.
luigifab
4

JS Fiddle: http://jsfiddle.net/eyedean/1bw357kw/

popFileSelector = function() {
    var el = document.getElementById("fileElem");
    if (el) {
        el.click();  
    }
};

window.popRightAway = function() {
    document.getElementById('log').innerHTML += 'I am right away!<br />';
    popFileSelector();
};

window.popWithDelay = function() {
    document.getElementById('log').innerHTML += 'I am gonna delay!<br />';
    window.setTimeout(function() {
        document.getElementById('log').innerHTML += 'I was delayed!<br />';
        popFileSelector();
    }, 1000);
};
<body>
  <form>
      <input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)" />
  </form>
  <a onclick="popRightAway()" href="#">Pop Now</a>
    <br />
  <a onclick="popWithDelay()" href="#">Pop With 1 Second Delay</a>
    <div id="log">Log: <br /></div>
</body>

Auxílio em
fonte
3

Este código funciona para mim. É isso que você está tentando fazer?

<input type="file" style="position:absolute;left:-999px;" id="fileinput" />
<button  id="addfiles" >Add files</button>

<script language="javascript" type="text/javascript">
   $("#addfiles").click(function(){
      $("#fileinput").click();
   });
</script>
Mat J
fonte
3

Minha solução para o Safari com jQuery e jQuery-ui:

$("<input type='file' class='ui-helper-hidden-accessible' />").appendTo("body").focus().trigger('click');
Adeus
fonte
heads up: não funciona para o firefox 33, chrome 38. trigger ('click') pode funcionar apenas dentro do contexto de eventos de interação do usuário - manipuladores de eventos.
Amit G
3

Aqui está a solução JavaScript pura para esse problema. Funciona bem em todos os navegadores

<script>
    function upload_image_init(){
        var elem = document.getElementById('file');
        if(elem && document.createEvent) {
           var evt = document.createEvent("MouseEvents");
           evt.initEvent("click", true, false);
           elem.dispatchEvent(evt);
        }
    }
</script>
Adam Fischer
fonte
2

Existem maneiras de redirecionar eventos para o controle, mas não espere poder disparar facilmente eventos para o controle de incêndio, pois os navegadores tentarão bloquear isso por (boas) razões de segurança.

Se você só precisa que a caixa de diálogo de arquivo apareça quando um usuário clica em algo, digamos que você queira botões de upload de arquivo com melhor aparência, dê uma olhada no que Shaun Inman inventou .

Consegui alcançar o acionamento do teclado com uma mudança criativa de foco dentro e fora do controle entre eventos de pressionamento de tecla, pressionamento de tecla e keyup. YMMV.

Meu conselho sincero é deixar isso em paz, porque este é um mundo de dor de incompatibilidade de navegador. Atualizações menores do navegador também podem bloquear truques sem aviso prévio, e você pode precisar reinventar os hacks para mantê-lo funcionando.

Borgar
fonte
2

Eu estava pesquisando isso há um tempo atrás, porque queria criar um botão personalizado que abrisse a caixa de diálogo do arquivo e iniciasse o upload imediatamente. Acabei de perceber algo que pode tornar isso possível - o Firefox parece abrir a caixa de diálogo quando você clica em qualquer lugar do upload. Portanto, o seguinte pode ser feito:

  1. Crie um upload de arquivo e um elemento separado contendo uma imagem que você deseja usar como botão
  2. Organize-os para que se sobreponham e torne o elemento de arquivo backgroud e borda transparente, para que o botão seja a única coisa visível
  3. Adicione o javascript para fazer o IE abrir a caixa de diálogo ao clicar na entrada de botão / arquivo
  4. Use um evento onchange para enviar o formulário quando um arquivo for selecionado

Isso é apenas teórico, pois eu já usei outro método para resolver o problema, mas pode funcionar.

user83358
fonte
2

Eu tinha uma <input type="button">tag oculta. O que eu fiz foi anexar o"onClick" evento a qualquer componente visível de qualquer tipo, como um rótulo. Isso foi feito usando as Ferramentas de desenvolvedor do Google Chrome ou o Firebug do Mozilla Firefox usando o botão direito do mouse em "editar HTML". Nesse evento, especifique o seguinte script ou algo semelhante:

Se você possui JQuery:

$('#id_of_component').click();

se não:

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

Obrigado.

Kurt
fonte
2

Ei, esta solução funciona. para download, deveríamos estar usando o MSBLOB

$scope.getSingleInvoicePDF = function(invoiceNumberEntity) {
   var fileName = invoiceNumberEntity + ".pdf";
   var pdfDownload = document.createElement("a");
   document.body.appendChild(pdfDownload);

   AngularWebService.getFileWithSuffix("ezbillpdfget",invoiceNumberEntity,"pdf" ).then(function(returnedJSON) {
       var fileBlob = new Blob([returnedJSON.data], {type: 'application/pdf'});
       if (navigator.appVersion.toString().indexOf('.NET') > 0) { // for IE browser
           window.navigator.msSaveBlob(fileBlob, fileName);
       } else { // for other browsers
           var fileURL = window.URL.createObjectURL(fileBlob);
           pdfDownload.href = fileURL;
           pdfDownload.download = fileName;
           pdfDownload.click();      
       }
   });
};

Para AngularJS ou mesmo para javascript normal.

Shankar Shastri
fonte
1

Agora isso será possível no Firefox 4, com a ressalva de que ele conta como uma janela pop-up e, portanto, será bloqueado sempre que uma janela pop-up tiver sido.

Neil
fonte
2
Na verdade, o firefox4 melhora muito o estado do upload de arquivos. Meu problema é como fazer o mesmo no navegador Google Chrome.
erick2red
1

Aqui está a solução que funciona para mim: CSS:

#uploadtruefield {
    left: 225px;
    opacity: 0;
    position: absolute;
    right: 0;
    top: 266px;
    opacity:0;
    -moz-opacity:0;
    filter:alpha(opacity:0);
    width: 270px;
    z-index: 2;
}

.uploadmask {
    background:url(../img/browse.gif) no-repeat 100% 50%;
}
#uploadmaskfield{
    width:132px;
}

HTML com ajuda "pequena" do JQuery:

<div class="uploadmask">
    <input id="uploadmaskfield" type="text" name="uploadmaskfield">
</div>
<input id="uploadtruefield"  type="file" onchange="$('#uploadmaskfield').val(this.value)" >

Apenas certifique-se de que o maskfied seja coberto de maneira completa pelo verdadeiro campo de upload.

DejanR
fonte
1

Você pode fazer isso conforme a resposta da caixa de diálogo Abrir arquivo na tag <a>

<input type="file" id="upload" name="upload" style="visibility: hidden; width: 1px;     height: 1px" multiple />
<a href="" onclick="document.getElementById('upload').click(); return false">Upload</a>
McLosys Creative
fonte
1

Descobri que se a entrada (arquivo) estiver fora do formulário, o acionamento do evento click invocará a caixa de diálogo do arquivo.

Nikita
fonte
1

Espero que isso ajude alguém - passei duas horas batendo minha cabeça contra ele:

No IE8 ou IE9, se você acionar a abertura de uma entrada de arquivo com javascript de qualquer forma (acredite, eu tentei todas elas), ele não permitirá que você envie o formulário usando javascript, apenas falhará silenciosamente.

Enviar o formulário através de um botão de envio regular pode funcionar, mas chamar form.submit (); falhará silenciosamente.

Eu tive que recorrer a sobrepor meu botão de seleção de arquivo com uma entrada de arquivo transparente que funcione.

Gálatas
fonte
Além disso, você pode agrupar a entrada do arquivo em um rótulo para que no IE você possa obter a área clicável do rótulo para cobrir toda a região do botão, enquanto que com apenas uma tag de entrada de arquivo, apenas metade é clicável no IE.
Gálatas
1

Isso funcionou para mim:

<script>
    function sel_file() {
        $("input[name=userfile]").trigger('click');
    }  
</script>

<input type="file" name="userfile" id="userfile" />

<a href="javascript:sel_file();">Click</a>
joan16v
fonte
1

Não é impossível:

var evObj = document.createEvent('MouseEvents');
evObj.initMouseEvent('click', true, true, window);  
setTimeout(function(){ document.getElementById('input_field_id').dispatchEvent(evObj); },100);

Mas, de alguma forma, funciona apenas se estiver em uma função chamada por meio de um evento de clique.

Então você pode ter a seguinte configuração:

html:

<div onclick="openFileChooser()" class="some_fancy_stuff">Click here to open image chooser</div>
<input type="file" id="input_img">

JavaScript:

    function openFileChooser() {
      var evObj = document.createEvent('MouseEvents');
      evObj.initMouseEvent('click', true, true, window);  
      setTimeout(function()
      { 
        document.getElementById('input_img').dispatchEvent(evObj);      
      },100);      
    }
EscapeNetscape
fonte
O createEvent()e initMouseEvent()foi preterido. Você tem que usar um CustomEvent()agora ...
Alexis Wilke
0

Você pode usar

<button id="file">select file</button>
<input type="file" name="file" id="file_input" style="display:none;">
<script>
$('#file').click(function() {
        $('#file_input').focus().trigger('click');
    });
</script>
Avanish Kumar
fonte