Como posso detectar quando o usuário cancela uma entrada de arquivo usando uma entrada de arquivo html?
onChange me permite detectar quando eles escolhem um arquivo, mas eu também gostaria de saber quando eles cancelam (feche a caixa de diálogo de escolha de arquivo sem selecionar nada).
e.target.files
Respostas:
Embora não seja uma solução direta, e também ruim por funcionar apenas (pelo que testei) com onfocus (exigindo um bloqueio de evento bastante limitador), você pode obtê-lo com o seguinte:
O que é bom nisso, é que você pode anexar / desanexar no tempo com o evento de arquivo, e também parece funcionar bem com entradas ocultas (uma vantagem definitiva se você estiver usando uma solução alternativa visual para a entrada padrão de baixa qualidade type = ' Arquivo'). Depois disso, você só precisa descobrir se o valor de entrada mudou.
Um exemplo:
Veja-o em ação: http://jsfiddle.net/Shiboe/yuK3r/6/
Infelizmente, parece funcionar apenas em navegadores webkit. Talvez outra pessoa possa descobrir a solução do firefox / IE
fonte
addEventListener("focus",fn,{once: true})
. No entanto, não consegui disparar usandodocument.body.addEventListener
.. Não sei por quê. Em vez disso, obtive o mesmo resultado comwindow.addEventListener
.mousemove
evento nowindow.document
além para ouvirfocus
sobrewindow
parece funcionar em todos os lugares (pelo menos em navegadores modernos, eu não me importo com destroços passado-década). Basicamente, qualquer evento de interação pode ser usado para esta tarefa que é bloqueado por uma caixa de diálogo de abertura de arquivo aberta.Você não pode.
O resultado da caixa de diálogo do arquivo não é exposto ao navegador.
fonte
change
evento é despachado.change
evento será despachado para isso.Então, vou jogar meu chapéu nessa questão, já que encontrei uma nova solução. Eu tenho um Progressive Web App que permite aos usuários capturar fotos e vídeos e carregá-los. Usamos WebRTC quando possível, mas recorremos a seletores de arquivo HTML5 para dispositivos com menos suporte * tosse Safari tosse *. Se você estiver trabalhando especificamente em um aplicativo da web móvel Android / iOS que usa a câmera nativa para capturar fotos / vídeos diretamente, esta é a melhor solução que encontrei.
O ponto crucial desse problema é que, quando a página carrega, o
file
énull
, mas quando o usuário abre a caixa de diálogo e pressiona "Cancelar", ofile
énull
, portanto, não "mudou", portanto, nenhum evento de "alteração" é acionado. Para desktops, isso não é tão ruim porque a maioria das IUs de desktops não dependem de saber quando um cancelamento é invocado, mas as IUs móveis que ativam a câmera para capturar uma foto / vídeo dependem muito de saber quando um cancelamento é pressionado.Eu usei originalmente o
document.body.onfocus
evento para detectar quando o usuário retornou do seletor de arquivos, e isso funcionou para a maioria dos dispositivos, mas o iOS 11.3 o quebrou porque o evento não é acionado.Conceito
Minha solução para isso é * tremor * para medir o tempo da CPU para determinar se a página está atualmente em primeiro ou segundo plano. Em dispositivos móveis, o tempo de processamento é atribuído ao aplicativo atualmente em primeiro plano. Quando uma câmera está visível, ela rouba o tempo da CPU e diminui a prioridade do navegador. Tudo o que precisamos fazer é medir quanto tempo de processamento é dado à nossa página, quando a câmera for iniciada, nosso tempo disponível cairá drasticamente. Quando a câmera é dispensada (cancelada ou não), nosso tempo disponível aumenta.
Implementação
Podemos medir o tempo da CPU usando
setTimeout()
para invocar um retorno de chamada em X milissegundos e, em seguida, medir quanto tempo levou para invocá-lo de fato. O navegador nunca o invocará exatamente após X milissegundos, mas se estiver razoavelmente perto, devemos estar em primeiro plano. Se o navegador estiver muito longe (mais de 10 vezes mais lento do que o solicitado), devemos estar em segundo plano. Uma implementação básica disso é assim:Eu testei isso em uma versão recente do iOS e Android, trazendo a câmera nativa definindo os atributos no
<input />
elemento.Na verdade, isso funciona muito melhor do que eu esperava. Ele executa 10 testes solicitando que um cronômetro seja chamado em 25 milissegundos. Em seguida, mede quanto tempo realmente levou para invocar e, se a média de 10 tentativas for inferior a 50 milissegundos, presumimos que devemos estar em primeiro plano e a câmera sumiu. Se for maior que 50 milissegundos, ainda devemos estar em segundo plano e continuar aguardando.
Alguns detalhes adicionais
Usei em
setTimeout()
vez desetInterval()
porque o último pode enfileirar várias invocações que são executadas imediatamente uma após a outra. Isso poderia aumentar drasticamente o ruído em nossos dados, então continueisetTimeout()
, embora seja um pouco mais complicado de fazer.Esses números específicos funcionaram bem para mim, embora eu tenha visto pelo menos uma vez em que o descarte da câmera foi detectado prematuramente. Acredito que isso seja porque a câmera pode demorar para abrir e o dispositivo pode executar 10 tentativas antes de realmente entrar em segundo plano. Adicionar mais tentativas ou esperar cerca de 25-50 milissegundos antes de iniciar esta função pode ser uma solução alternativa para isso.
Área de Trabalho
Infelizmente, isso realmente não funciona para navegadores de desktop. Em teoria, o mesmo truque é possível, pois eles priorizam a página atual sobre as páginas em segundo plano. No entanto, muitos desktops têm recursos suficientes para manter a página em execução em velocidade total, mesmo quando em segundo plano, então essa estratégia não funciona na prática.
Soluções alternativas
Uma solução alternativa que poucas pessoas mencionam e que explorei foi zombar de a
FileList
. Começamos comnull
no<input />
e, em seguida, se o usuário abrir a câmera e cancelar, eles voltamnull
, o que não é uma alteração e nenhum evento será acionado. Uma solução seria atribuir um arquivo fictício ao<input />
início da página, portanto, definir comonull
seria uma alteração que acionaria o evento apropriado.Infelizmente, não existe uma maneira oficial de criar um
FileList
, e o<input />
elemento requer umFileList
em particular e não aceitará nenhum outro valor além dissonull
. Naturalmente, osFileList
objetos não podem ser construídos diretamente, devido a algum velho problema de segurança que aparentemente não é mais relevante. A única maneira de obter alguém de fora de um<input />
elemento é utilizar um hack que copia e cola dados para simular um evento da área de transferência que pode conter umFileList
objeto (você está basicamente fingindo arrastar e soltar um arquivo evento do seu site). Isso é possível no Firefox, mas não para iOS Safari, portanto, não era viável para meu caso de uso específico.Navegadores, por favor ...
Nem é preciso dizer que isso é ridículo. O fato de que as páginas da web não recebem nenhuma notificação de que um elemento crítico da IU foi alterado é simplesmente ridículo. Este é realmente um bug na especificação, já que nunca foi planejado para uma IU de captura de mídia em tela inteira, e não acionar o evento "alterar" é tecnicamente uma especificação.
No entanto , os fornecedores de navegadores podem reconhecer a realidade disso? Isso pode ser resolvido com um novo evento "concluído", que é acionado mesmo quando nenhuma alteração ocorre, ou você pode simplesmente acionar "alteração" de qualquer maneira. Sim, isso seria contra a especificação, mas é trivial para mim desduplicar um evento de alteração no lado do JavaScript, mas fundamentalmente impossível inventar meu próprio evento "concluído". Mesmo minha solução é realmente apenas heurística, se não oferecer garantias sobre o estado do navegador.
Do jeito que está, essa API é fundamentalmente inutilizável para dispositivos móveis e acho que uma mudança relativamente simples no navegador poderia tornar isso infinitamente mais fácil para os desenvolvedores da web * sair da caixa de sabão *.
fonte
Quando você seleciona um arquivo e clica em abrir / cancelar, o
input
elemento deve perder o foco, também conhecido comoblur
. Assumindo que a inicialvalue
deinput
está vazia, qualquer valor não vazio em seublur
manipulador indicaria um OK e um valor vazio significaria um Cancelar.ATUALIZAÇÃO: O
blur
não é acionado quando oinput
está oculto. Portanto, não é possível usar esse truque com uploads baseados em IFRAME, a menos que você queira exibir temporariamente oinput
.fonte
blur
não é acionado quando você seleciona um arquivo. Não tentei em outros navegadores.fonte
else
declaração foi avaliada?A maioria dessas soluções não funciona para mim.
O problema é que você nunca sabe qual evento será disparado punho, é
click
ou échange
? Você não pode assumir nenhuma ordem, porque provavelmente depende da implementação do navegador.Pelo menos no Opera e no Chrome (final de 2015)
click
é acionado um pouco antes de 'preencher' a entrada com arquivos, então você nunca saberá a duraçãofiles.length != 0
até o atrasoclick
para ser acionado depoischange
.Aqui está o código:
fonte
Apenas ouça o evento de clique também.
Seguindo o exemplo de Shiboe, aqui está um exemplo de jQuery:
Você pode vê-lo em ação aqui: http://jsfiddle.net/T3Vwz
fonte
Você pode pegar o cancelamento se escolher o mesmo arquivo anterior e clicar em cancelar: neste caso.
Você pode fazer assim:
fonte
A maneira mais fácil é verificar se há algum arquivo na memória temporária. Se você deseja obter o evento de mudança toda vez que o usuário clica na entrada do arquivo, você pode acioná-lo.
fonte
A solução de Shiboe seria boa se funcionasse no webkit móvel, mas não funciona. O que posso fazer é adicionar um ouvinte de evento mousemove a algum objeto dom no momento em que a janela de entrada do arquivo é aberta, assim:
O evento mousemove é bloqueado na página enquanto a caixa de diálogo do arquivo está aberta e, quando fechada, verifica se há algum arquivo na entrada do arquivo. No meu caso, quero um indicador de atividade bloqueando as coisas até que o arquivo seja carregado, então só quero remover meu indicador no cancelamento.
No entanto, isso não resolve para o celular, já que não há mouse para mover. Minha solução não é perfeita, mas acho que é boa o suficiente.
Agora estamos ouvindo um toque na tela para fazer a mesma verificação de arquivos. Tenho certeza de que o dedo do usuário será colocado na tela rapidamente após cancelar e descartar este indicador de atividade.
Pode-se também adicionar o indicador de atividade no evento de alteração de entrada de arquivo, mas no celular geralmente há alguns segundos de atraso entre a seleção da imagem e o disparo do evento de alteração, então é muito melhor UX para o indicador de atividade a ser exibido no início do processo.
fonte
Achei esse atributo, é mais simples ainda.
Aqui
selectedFile
está uminput type=file
.fonte
Sei que esta é uma pergunta muito antiga, mas caso ajude alguém, descobri ao usar o evento onmousemove para detectar o cancelamento, que era necessário testar dois ou mais eventos em um curto espaço de tempo. Isso ocorreu porque eventos onmousemove únicos são gerados pelo navegador (Chrome 65) cada vez que o cursor é movido para fora da janela de diálogo de arquivo de seleção e cada vez que é movido para fora da janela principal e de volta para dentro. Um contador simples de eventos de movimento do mouse juntamente com um tempo limite de curta duração para zerar o contador, funcionou muito bem.
fonte
No meu caso, tive que ocultar o botão enviar enquanto os usuários selecionavam as imagens.
Isso é o que eu aponto:
#image-field
é meu seletor de arquivo. Quando alguém clica nele, eu desabilito o botão de envio do formulário. O que quero dizer é que, quando a caixa de diálogo de arquivo é fechada - não importa se eles selecionam um arquivo ou cancelam -#image-field
recebe o foco de volta, então ouço esse evento.ATUALIZAR
Descobri que isso não funciona em safari e poltergeist / phantomjs. Leve essa informação em consideração se quiser implementá-la.
fonte
Combinando as soluções Shiboe's e alx's, tenho o código mais confiável:
Geralmente, o evento mousemove resolve, mas caso o usuário clique e:
... não teremos o evento mousemove, portanto, nenhum retorno de chamada cancelado. Além disso, se o usuário cancelar a segunda caixa de diálogo e mover o mouse, obteremos 2 callbacks de cancelamento. Felizmente, o evento especial do jQuery focusIn surge no documento em ambos os casos, ajudando-nos a evitar tais situações. A única limitação é bloquear o evento focusIn também.
fonte
mousemove
eventodocument
durante a seleção de arquivos na caixa de diálogo do sistema operacional no Chrome 56.0.2924.87 no Ubuntu 16.10. Sem problemas, quando não movo o mouse, ou use apenas o teclado para selecionar o arquivo. Tive que substituirmousemove
porkeydown
para detectar o cancelamento o mais cedo possível, mas não "muito cedo", quando a caixa de diálogo ainda estava aberta.Vejo que minha resposta seria bastante desatualizada, mas nunca menos. Eu enfrentei o mesmo problema. Então aqui está minha solução. O código mais útil cortado foi o do KGA. Mas não está funcionando totalmente e é um pouco complicado. Mas eu simplifiquei.
Além disso, o principal causador de problemas era o fato de que o evento de 'mudança' não ocorre instantaneamente após o foco, então temos que esperar algum tempo.
"#appendfile" - no qual o usuário clica para anexar um novo arquivo. Hrefs obtém eventos de foco.
fonte
Você pode detectar isso apenas em circunstâncias limitadas. Especificamente, no Chrome, se um arquivo foi selecionado anteriormente e a caixa de diálogo do arquivo é clicada e cancelada, o Chrome limpa o arquivo e dispara o evento onChange.
https://code.google.com/p/chromium/issues/detail?id=2508
Nesse cenário, você pode detectar isso manipulando o evento onChange e verificando a propriedade de arquivos .
fonte
O seguinte parece funcionar para mim (no desktop, windows):
Isso usa angularjs $ q, mas você deve ser capaz de substituí-lo por qualquer outra estrutura de promessa, se necessário.
Testado no IE11, Edge, Chrome, Firefox, mas parece não funcionar no Chrome em um tablet Android, pois não dispara o evento Focus.
fonte
O
file
campo -type, frustrantemente, não responde a muitos eventos (desfoque seria ótimo). Eu vejo muitas pessoas sugerindochange
soluções orientadas e sendo rejeitadas.change
funciona, mas tem uma grande falha (vs o que queremos que aconteça).Ao carregar recentemente uma página que contém um campo de arquivo, abra a caixa e pressione cancelar. Nada muda , frustrantemente .
O que escolhi fazer é carregar em um estado fechado.
section#after-image
no meu caso, está oculta. Quando minhasfile field
alterações, um botão de upload é mostrado. Após o upload bem-sucedido,section#after-image
é mostrado.change
evento é acionado por este cancelamento, e aí eu posso (e faço) ocultar novamente meu botão de upload até que um arquivo adequado seja selecionado.Tive a sorte de que este estado fechado já fosse o desenho do meu formulário. Você não precisa usar o mesmo estilo, simplesmente mantendo o botão de upload inicialmente oculto e, após a alteração, definindo um campo oculto ou variável javascript para algo que você possa monitorar no envio.
Tentei alterar o valor de files [0] antes de interagir com o campo. Isso não fez nada em relação ao onchange.
Então, sim,
change
funciona, pelo menos tão bom quanto vamos conseguir. O campo de arquivos está protegido, por razões óbvias, mas para a frustração de desenvolvedores bem-intencionados.Não é adequado ao meu propósito, mas você pode ser capaz de
onclick
carregar um prompt de aviso (não umalert()
, porque isso interrompe o processamento da página) e ocultá-lo se a alteração for acionada e o arquivo [0] for nulo. Se a mudança não for acionada, o div permanecerá em seu estado.fonte
Existe uma maneira hackeada de fazer isso (adicionar callbacks ou resolver alguma implementação adiada / prometida em vez de
alert()
chamadas):Como funciona: enquanto a caixa de diálogo de seleção de arquivo está aberta, o documento não recebe eventos do ponteiro do mouse. Há um atraso de 1000 ms para permitir que o diálogo realmente apareça e bloqueie a janela do navegador. Verificado no Chrome e Firefox (somente Windows).
Mas essa não é uma maneira confiável de detectar diálogos cancelados, é claro. Porém, pode melhorar algum comportamento da IU para você.
fonte
Aqui está minha solução, usando o foco de entrada de arquivo (sem usar nenhum temporizador)
fonte
Error: { "message": "Uncaught SyntaxError: missing ) after argument list", "filename": "https://stacksnippets.net/js", "lineno": 51, "colno": 1 }
Isso é hacky na melhor das hipóteses, mas aqui está um exemplo prático da minha solução para detectar se um usuário carregou ou não um arquivo, e apenas permitindo que ele prossiga se ele tiver carregado um arquivo.
Basicamente esconder o
Continue
,Save
,Proceed
ou qualquer que seja o seu botão é. Então, no JavaScript, você pega o nome do arquivo. Se o nome do arquivo não tiver um valor, não mostre oContinue
botão. Se tiver um valor, mostre o botão. Isso também funciona se eles primeiro fizerem upload de um arquivo e, em seguida, tentarem fazer upload de um arquivo diferente e clicarem em cancelar.Aqui está o código.
HTML:
JavaScript:
CSS:
Para o CSS, muito disso é tornar o site e o botão acessíveis para todos. Defina o estilo do seu botão como quiser.
fonte
Bem, isso não responde exatamente à sua pergunta. Minha suposição é que, você tem um cenário, quando adiciona uma entrada de arquivo e invoca a seleção de arquivo e, se o usuário clicar em cancelar, você apenas remove a entrada.
Se for esse o caso, então: Por que adicionar entrada de arquivo vazio?
Crie um na hora, mas adicione-o ao DOM apenas quando estiver preenchido. Assim:
Portanto, aqui eu crio <input type = "file" /> dinamicamente, vinculo a seu evento de alteração e invoco imediatamente o clique. A alteração será acionada apenas quando o usuário selecionar um arquivo e clicar em Ok, caso contrário, a entrada não será adicionada ao DOM e, portanto, não será enviada.
Exemplo de trabalho aqui: https://jsfiddle.net/69g0Lxno/3/
fonte
// Use pairar em vez de desfocar
fonte
Se você já requer JQuery, esta solução pode fazer o trabalho (este é exatamente o mesmo código que eu realmente precisava no meu caso, embora usar uma promessa seja apenas para forçar o código a esperar até que a seleção do arquivo seja resolvida):
});
fonte
Existem várias soluções propostas neste tópico e esta dificuldade em detectar quando o usuário clica no botão "Cancelar" na caixa de seleção de arquivos é um problema que afeta muitas pessoas.
O fato é que não existe uma maneira 100% confiável de detectar se o usuário clicou no botão "Cancelar" na caixa de seleção de arquivos. Mas existem maneiras de detectar com segurança se o usuário adicionou um arquivo ao arquivo de entrada. Portanto, esta é a estratégia básica desta resposta!
Decidi adicionar esta resposta porque aparentemente as outras respostas não funcionam na maioria dos navegadores ou garantidas em dispositivos móveis.
Resumidamente, o código é baseado em 3 pontos:
Para uma melhor compreensão, veja o código abaixo e também as notas.
Obrigado! = D
fonte
Conseguimos em angular como abaixo.
HTML
TS
fonte
Basta adicionar o ouvinte 'alterar' em sua entrada cujo tipo é arquivo. ie
Eu usei jQuery e obviamente qualquer um pode usar valina JS (conforme o requisito).
fonte
.change()
não é chamado de forma confiável se o usuário clicar em "cancelar" no seletor de arquivos.fonte
Solução para seleção de arquivo com entrada oculta
Nota: este código não detecta o cancelamento, ele oferece uma maneira de contornar a necessidade de detectá-lo em um caso comum em que as pessoas tentam detectá-lo.
Eu cheguei aqui enquanto procurava uma solução para uploads de arquivos usando uma entrada oculta, acredito que este seja o motivo mais comum para procurar uma maneira de detectar o cancelamento da entrada de arquivo (caixa de diálogo abrir arquivo -> se um arquivo foi selecionado, execute alguns código, caso contrário, não faça nada), aqui está minha solução:
Exemplo de uso:
Observe que se nenhum arquivo foi selecionado, a primeira linha retornará apenas uma vez
selectFile()
for chamada novamente (ou se você ligoufileSelectorResolve()
de outro lugar).Outro exemplo:
fonte
Você pode fazer um ouvinte de mudança jquery no campo de entrada e detectar que o usuário cancelou ou fechou a janela de upload pelo valor do campo.
aqui está um exemplo:
fonte