¿Cómo puedo detectar cuando el usuario cancela una entrada de archivo usando una entrada de archivo html?
onChange me permite detectar cuándo eligen un archivo, pero también me gustaría saber cuándo cancelan (cierre el cuadro de diálogo de selección de archivo sin seleccionar nada).
e.target.files
Respuestas:
Si bien no es una solución directa, y también es mala porque solo (por lo que he probado) funciona con onfocus (que requiere un bloqueo de eventos bastante limitante), puede lograrlo con lo siguiente:
Lo bueno de esto es que puede adjuntarlo / separarlo a tiempo con el evento de archivo, y también parece funcionar bien con entradas ocultas (una ventaja definitiva si está utilizando una solución visual para el tipo de entrada predeterminado de mierda = ' expediente'). Después de eso, solo necesita averiguar si el valor de entrada cambió.
Un ejemplo:
Véalo en acción: http://jsfiddle.net/Shiboe/yuK3r/6/
Lamentablemente, solo parece funcionar en navegadores webkit. Tal vez alguien más pueda descubrir la solución firefox / IE
fuente
addEventListener("focus",fn,{once: true})
. Sin embargo, no pude hacer que se disparara usandodocument.body.addEventListener
... No sé por qué. En cambio, obtuve el mismo resultado conwindow.addEventListener
.mousemove
caso dewindow.document
, además de escucharfocus
elwindow
parece funcionar en todas partes (al menos en los navegadores modernos, no me importa acerca de los pecios más allá de década). Básicamente, cualquier evento de interacción se puede utilizar para esta tarea que esté bloqueado por un diálogo de apertura de archivo abierto.No puedes.
El resultado del cuadro de diálogo del archivo no se expone al navegador.
fuente
change
se envía ningún evento.change
se envía ningún evento para ese archivo .Así que me meteré en esta pregunta ya que se me ocurrió una solución novedosa. Tengo una aplicación web progresiva que permite a los usuarios capturar fotos y videos y subirlos. Usamos WebRTC cuando es posible, pero recurrimos a los selectores de archivos HTML5 para dispositivos con menos soporte * tos de Safari *. Si está trabajando específicamente en una aplicación web móvil Android / iOS que usa la cámara nativa para capturar fotos / videos directamente, entonces esta es la mejor solución que he encontrado.
El quid de este problema es que cuando se carga la página, el
file
esnull
, pero cuando el usuario abre el cuadro de diálogo y presiona "Cancelar", elfile
es todavíanull
, por lo que no "cambió", por lo que no se activa ningún evento de "cambio". Para las computadoras de escritorio, esto no es tan malo porque la mayoría de las interfaces de usuario de escritorio no dependen de saber cuándo se invoca una cancelación, pero las UI móviles que muestran la cámara para capturar una foto / video dependen mucho de saber cuándo se presiona una cancelación.Originalmente usé el
document.body.onfocus
evento para detectar cuándo el usuario regresó del selector de archivos, y esto funcionó para la mayoría de los dispositivos, pero iOS 11.3 lo rompió ya que ese evento no se activa.Concepto
Mi solución a esto es * estremecimiento * para medir el tiempo de la CPU para determinar si la página está actualmente en primer plano o en segundo plano. En los dispositivos móviles, el tiempo de procesamiento se asigna a la aplicación que se encuentra actualmente en primer plano. Cuando una cámara es visible, robará tiempo de CPU y despriorizará al navegador. Todo lo que tenemos que hacer es medir cuánto tiempo de procesamiento se le da a nuestra página, cuando se inicia la cámara, nuestro tiempo disponible se reducirá drásticamente. Cuando se desconecta la cámara (ya sea cancelada o no), nuestro tiempo disponible aumenta de nuevo.
Implementación
Podemos medir el tiempo de la CPU usando
setTimeout()
para invocar una devolución de llamada en X milisegundos y luego medir cuánto tiempo tomó realmente invocarla. El navegador nunca lo invocará exactamente después de X milisegundos, pero si está razonablemente cerca, entonces debemos estar en primer plano. Si el navegador está muy lejos (más de 10 veces más lento de lo solicitado) entonces debemos estar en segundo plano. Una implementación básica de esto es así:Probé esto en una versión reciente de iOS y Android, abriendo la cámara nativa configurando los atributos en el
<input />
elemento.En realidad, esto funciona mucho mejor de lo que esperaba. Ejecuta 10 pruebas solicitando que se invoque un temporizador en 25 milisegundos. Luego mide cuánto tiempo realmente tomó invocar, y si el promedio de 10 intentos es menos de 50 milisegundos, asumimos que debemos estar en primer plano y la cámara no está. Si es superior a 50 milisegundos, entonces todavía debemos estar en segundo plano y debemos seguir esperando.
Algunos detalles adicionales
Usé en
setTimeout()
lugar desetInterval()
porque este último puede poner en cola múltiples invocaciones que se ejecutan inmediatamente una después de la otra. Esto podría aumentar drásticamente el ruido en nuestros datos, así que me quedésetTimeout()
a pesar de que es un poco más complicado hacerlo.Estos números en particular funcionaron bien para mí, aunque he visto al menos una instancia en la que el disparo de la cámara se detectó prematuramente. Creo que esto se debe a que la cámara puede tardar en abrirse y el dispositivo puede ejecutar 10 pruebas antes de que se vuelva a poner en segundo plano. Agregar más pruebas o esperar entre 25 y 50 milisegundos antes de iniciar esta función puede ser una solución alternativa.
Escritorio
Desafortunadamente, esto realmente no funciona para los navegadores de escritorio. En teoría, el mismo truco es posible, ya que priorizan la página actual sobre las páginas en segundo plano. Sin embargo, muchos escritorios tienen suficientes recursos para mantener la página funcionando a toda velocidad incluso cuando están en segundo plano, por lo que esta estrategia no funciona en la práctica.
Soluciones alternativas
Una solución alternativa que no mucha gente menciona que exploré fue burlarme de un
FileList
. Comenzamos connull
en<input />
y luego, si el usuario abre la cámara y cancela, regresanull
, lo cual no es un cambio y no se activará ningún evento. Una solución sería asignar un archivo ficticio al<input />
inicio de la página, por lo tanto, establecer ennull
sería un cambio que desencadenaría el evento apropiado.Desafortunadamente, no hay forma oficial de crear un
FileList
, y el<input />
elemento requiere unFileList
en particular y no aceptará ningún otro valor ademásnull
. Naturalmente, losFileList
objetos no se pueden construir directamente, debido a algún problema de seguridad antiguo que aparentemente ya no es relevante. La única forma de conseguir uno fuera de un<input />
elemento es utilizar un truco que copia y pega datos para simular un evento de portapapeles que puede contener unFileList
objeto (básicamente estás fingiendo arrastrar y soltar un archivo en evento de su sitio web). Esto es posible en Firefox, pero no para iOS Safari, por lo que no era viable para mi caso de uso particular.Navegadores, por favor ...
No hace falta decir que esto es evidentemente ridículo. El hecho de que a las páginas web no se les notifique que un elemento crítico de la interfaz de usuario ha cambiado es simplemente ridículo. Esto es realmente un error en la especificación, ya que nunca fue diseñado para una interfaz de usuario de captura de medios a pantalla completa, y no activar el evento de "cambio" es técnicamente conforme a las especificaciones.
Sin embargo , ¿pueden los proveedores de navegadores reconocer la realidad de esto? Esto podría resolverse con un nuevo evento "hecho" que se activa incluso cuando no se produce ningún cambio, o simplemente puede activar "cambio" de todos modos. Sí, eso iría en contra de las especificaciones, pero es trivial para mí deducir un evento de cambio en el lado de JavaScript, aunque fundamentalmente imposible inventar mi propio evento "hecho". Incluso mi solución es realmente solo heurística, si no ofrezco garantías sobre el estado del navegador.
Tal como está, esta API es fundamentalmente inutilizable para dispositivos móviles, y creo que un cambio de navegador relativamente simple podría hacer esto infinitamente más fácil para los desarrolladores web * pasos fuera de la caja de jabón *.
fuente
Cuando selecciona un archivo y hace clic en abrir / cancelar, el
input
elemento debería perder el foco, también conocido comoblur
. Suponiendo que la inicialvalue
deinput
está vacía, cualquier valor no vacío en sublur
controlador indicaría un OK, y un valor vacío significaría un Cancelar.ACTUALIZACIÓN:
blur
no se activa cuandoinput
está oculto. Por lo tanto, no puede usar este truco con cargas basadas en IFRAME, a menos que desee mostrar temporalmente elinput
.fuente
blur
no se activa cuando selecciona un archivo. No lo he probado en otros navegadores.fuente
else
alguna vez se evalúa su estado de cuenta?La mayoría de estas soluciones no me funcionan.
El problema es que nunca se sabe qué evento se desencadenará, ¿verdad
click
o nochange
? No puede asumir ningún orden, porque probablemente depende de la implementación del navegador.Al menos en Opera y Chrome (finales de 2015)
click
se activa justo antes de 'llenar' la entrada con archivos, por lo que nunca sabrá la duraciónfiles.length != 0
hasta que se demoreclick
en activarse despuéschange
.Aquí está el código:
fuente
Solo escuche el evento de clic también.
Siguiendo el ejemplo de Shiboe, aquí hay un ejemplo de jQuery:
Puedes verlo en acción aquí: http://jsfiddle.net/T3Vwz
fuente
Puede capturar la cancelación si elige el mismo archivo que anteriormente y hace clic en cancelar: en este caso.
Puedes hacerlo así:
fuente
La forma más sencilla es comprobar si hay archivos en la memoria temporal. Si desea obtener el evento de cambio cada vez que el usuario hace clic en la entrada del archivo, puede activarlo.
fuente
La solución de Shiboe sería buena si funcionara en un webkit móvil, pero no es así. Lo que se me ocurre es agregar un detector de eventos mousemove a algún objeto dom en el momento en que se abre la ventana de entrada del archivo, así:
El evento mousemove se bloquea en la página mientras el cuadro de diálogo del archivo está abierto, y cuando se cierra, se comprueba si hay archivos en la entrada del archivo. En mi caso, quiero un indicador de actividad que bloquee las cosas hasta que se cargue el archivo, por lo que solo quiero eliminar mi indicador al cancelar.
Sin embargo, esto no resuelve para dispositivos móviles, ya que no hay mouse para mover. Mi solución no es perfecta, pero creo que es lo suficientemente buena.
Ahora estamos escuchando un toque en la pantalla para hacer la misma verificación de archivos. Estoy bastante seguro de que el dedo del usuario aparecerá en la pantalla con bastante rapidez después de cancelar y descartar este indicador de actividad.
También se podría simplemente agregar el indicador de actividad en el evento de cambio de entrada de archivo, pero en el dispositivo móvil a menudo hay un retraso de unos segundos entre la selección de la imagen y la activación del evento de cambio, por lo que es mucho mejor UX para que el indicador de actividad se muestre en el inicio del proceso.
fuente
Encontré este atributo, el más simple hasta ahora.
Aquí
selectedFile
hay uninput type=file
.fuente
Sé que esta es una pregunta muy antigua, pero en caso de que ayude a alguien, descubrí que al usar el evento onmousemove para detectar la cancelación, era necesario probar dos o más eventos de este tipo en un corto espacio de tiempo. Esto se debió a que el navegador (Chrome 65) genera eventos únicos onmousemove cada vez que el cursor se mueve fuera de la ventana de diálogo de selección de archivo y cada vez que se mueve fuera de la ventana principal y vuelve a entrar. Un contador simple de eventos de movimiento del mouse junto con un tiempo de espera de corta duración para restablecer el contador a cero funcionó de maravilla.
fuente
En mi caso, tuve que ocultar el botón de envío mientras los usuarios seleccionaban imágenes.
Esto es lo que se me ocurre:
#image-field
es mi selector de archivos. Cuando alguien hace clic en él, desactivo el botón de envío del formulario. El punto es que, cuando se cerró el cuadro de diálogo del archivo, no importa si seleccionan un archivo o cancelan,#image-field
recuperaron el enfoque, así que escucho ese evento.ACTUALIZAR
Descubrí que esto no funciona en safari y poltergeist / phantomjs. Tenga en cuenta esta información si desea implementarla.
fuente
Combinando las soluciones de Shiboe y alx, tengo el código más confiable:
Generalmente, el evento mousemove funciona, pero en caso de que el usuario haga un clic y luego:
... no obtendremos el evento mousemove, por lo tanto, no cancelaremos la devolución de llamada. Además, si el usuario cancela el segundo diálogo y hace un movimiento del mouse, obtendremos 2 devoluciones de llamada canceladas. Afortunadamente, el evento jQuery focusIn especial aparece en el documento en ambos casos, lo que nos ayuda a evitar tales situaciones. La única limitación es si uno bloquea el evento focusIn.
fuente
mousemove
eventodocument
durante la selección de archivos en el cuadro de diálogo del sistema operativo en Chrome 56.0.2924.87 en Ubuntu 16.10. No hay problema, cuando no mueva el mouse o use solo el teclado para seleccionar el archivo. Tuve que reemplazarmousemove
porkeydown
para detectar la cancelación lo antes posible, pero no "demasiado pronto", cuando el cuadro de diálogo aún estaba abierto.Veo que mi respuesta estaría bastante desactualizada, pero no por ello menos. Me enfrenté al mismo problema. Entonces esta es mi solución. El código recortado más útil fue el de KGA. Pero no funciona del todo y es un poco complicado. Pero lo simplifiqué.
Además, el principal causante de problemas fue el hecho de que el evento de 'cambio' no se produce instantáneamente después del enfoque, por lo que tenemos que esperar un tiempo.
"#appendfile": en el que el usuario hace clic para agregar un nuevo archivo. Los hrefs obtienen eventos de enfoque.
fuente
Puede detectar esto solo en circunstancias limitadas. Específicamente, en Chrome, si se seleccionó un archivo anteriormente y luego se hace clic en el cuadro de diálogo del archivo y se hace clic en Cancelar, Chrome borra el archivo y activa el evento onChange.
https://code.google.com/p/chromium/issues/detail?id=2508
En este escenario, puede detectar esto manejando el evento onChange y verificando la propiedad de los archivos .
fuente
Lo siguiente parece funcionar para mí (en el escritorio, Windows):
Esto usa angularjs $ q, pero debería poder reemplazarlo con cualquier otro marco de promesa si es necesario.
Probado en IE11, Edge, Chrome, Firefox, pero no parece funcionar en Chrome en una tableta Android ya que no activa el evento Focus.
fuente
El
file
campo -type, frustrantemente, no responde a muchos eventos (el desenfoque sería encantador). Veo a mucha gente sugiriendochange
soluciones orientadas a ellos y son rechazados.change
funciona, pero tiene un defecto importante (frente a lo que queremos que suceda).Cuando cargues una página que contenga un campo de archivo, abre el cuadro y presiona cancelar. Nada, frustrantemente, cambia .
Lo que elegí hacer es cargar en un estado cerrado.
section#after-image
en mi caso está oculta a la vista. Cuando mifile field
cambia, se muestra un botón de carga. Luego de una carga exitosa,section#after-image
se muestra.change
evento se activa mediante esta cancelación, y allí puedo (y lo hago) volver a ocultar mi botón de carga hasta que se seleccione un archivo adecuado.Tuve la suerte de que este estado cerrado ya fuera el diseño de mi formulario. No es necesario utilizar el mismo estilo, simplemente tener el botón de carga inicialmente oculto y, al cambiarlo, establecer un campo oculto o una variable de javascript en algo que pueda monitorear al enviar.
Intenté cambiar el valor de los archivos [0] antes de interactuar con el campo. Esto no hizo nada con respecto al cambio.
Así que sí,
change
funciona, al menos tan bien como vamos a conseguir. El campo de archivo está protegido, por razones obvias, pero para frustración de los desarrolladores bien intencionados.No se ajusta a mi propósito, pero es posible que pueda
onclick
, cargar un mensaje de advertencia (noalert()
, porque eso detiene el procesamiento de la página) y ocultarlo si se activa el cambio y los archivos [0] son nulos. Si no se activa el cambio, el div permanece en su estado.fuente
Hay una forma hackear de hacer esto (agregue devoluciones de llamada o resuelva alguna implementación diferida / promesa en lugar de
alert()
llamadas):Cómo funciona: mientras el diálogo de selección de archivos está abierto, el documento no recibe eventos de puntero del mouse. Hay un retraso de 1000 ms para permitir que el cuadro de diálogo aparezca y bloquee la ventana del navegador. Comprobado en Chrome y Firefox (solo Windows).
Pero esta no es una forma confiable de detectar diálogos cancelados, por supuesto. Sin embargo, podría mejorar el comportamiento de la interfaz de usuario.
fuente
Aquí está mi solución, usando el enfoque de entrada del archivo (sin usar ningún temporizador)
fuente
Error: { "message": "Uncaught SyntaxError: missing ) after argument list", "filename": "https://stacksnippets.net/js", "lineno": 51, "colno": 1 }
Esto es hacky en el mejor de los casos, pero aquí hay un ejemplo funcional de mi solución para detectar si un usuario ha subido un archivo o no, y solo permitirle continuar si ha subido un archivo.
Básicamente ocultar el
Continue
,Save
,Proceed
o lo que su botón es. Luego, en JavaScript, toma el nombre del archivo. Si el nombre del archivo no tiene un valor, no muestre elContinue
botón. Si tiene un valor, muestre el botón. Esto también funciona si al principio cargan un archivo y luego intentan cargar un archivo diferente y hacen clic en cancelar.Aquí está el código.
HTML:
JavaScript:
CSS:
Para CSS, mucho de esto es hacer que el sitio web y el botón sean accesibles para todos. Ajusta tu botón a lo que quieras.
fuente
Bueno, esto no responde exactamente a tu pregunta. Mi suposición es que, tiene un escenario, cuando agrega una entrada de archivo e invoca la selección de archivo, y si el usuario presiona cancelar, simplemente elimina la entrada.
Si este es el caso, entonces: ¿Por qué agregar una entrada de archivo vacía?
Cree el uno sobre la marcha, pero agréguelo a DOM solo cuando esté completo. Así:
Así que aquí creo <input type = "file" /> sobre la marcha, me vinculo a su evento de cambio y luego invoco inmediatamente el clic. El cambio se activará solo cuando el usuario seleccione un archivo y presione Aceptar; de lo contrario, la entrada no se agregará al DOM, por lo tanto, no se enviará.
Ejemplo de trabajo aquí: https://jsfiddle.net/69g0Lxno/3/
fuente
// Usa hover en lugar de difuminar
fuente
Si ya necesita JQuery, esta solución podría hacer el trabajo (este es exactamente el mismo código que realmente necesitaba en mi caso, aunque usar una Promesa es solo para forzar al código a esperar hasta que se resuelva la selección del archivo):
});
fuente
Hay varias soluciones propuestas en este hilo y esta dificultad para detectar cuando el usuario hace clic en el botón "Cancelar" en el cuadro de selección de archivos es un problema que afecta a muchas personas.
El hecho es que no existe una forma 100% confiable de detectar si el usuario ha hecho clic en el botón "Cancelar" en el cuadro de selección de archivos. Pero hay formas de detectar de manera confiable si el usuario ha agregado un archivo al archivo de entrada. ¡Así que esta es la estrategia básica de esta respuesta!
Decidí agregar esta respuesta porque aparentemente las otras respuestas no funcionan en la mayoría de los navegadores ni están garantizadas en los dispositivos móviles.
Brevemente, el código se basa en 3 puntos:
Para una mejor comprensión, mire el código a continuación y también las notas.
¡Gracias! = D
fuente
Logramos en angular como abajo.
HTML
TS
fuente
Simplemente agregue el oyente 'cambiar' en su entrada cuyo tipo es archivo. es decir
He terminado de usar jQuery y, obviamente, cualquiera puede usar valina JS (según el requisito).
fuente
.change()
no se llama de forma fiable si el usuario hace clic en "cancelar" en el selector de archivos.fuente
Solución para la selección de archivos con entrada oculta
Nota: este código no detecta la cancelación, ofrece una forma de eludir la necesidad de detectarlo en un caso común en el que las personas intentan detectarlo.
Llegué aquí mientras buscaba una solución para la carga de archivos usando una entrada oculta, creo que esta es la razón más común para buscar una forma de detectar la cancelación de la entrada del archivo (abrir diálogo de archivo -> si se seleccionó un archivo, ejecute algunos código, de lo contrario no haga nada), aquí está mi solución:
Ejemplo de uso:
Tenga en cuenta que si no se seleccionó ningún archivo, la primera línea volverá solo una vez que
selectFile()
se vuelva a llamar (o si llamófileSelectorResolve()
desde otro lugar).Otro ejemplo:
fuente
Puede hacer un oyente de cambios de jquery en el campo de entrada y detectar que el usuario cancela o cierra la ventana de carga por el valor del campo.
Aquí hay un ejemplo:
fuente