Tengo algunos menús HTML, que se muestran completamente cuando un usuario hace clic en el encabezado de estos menús. Me gustaría ocultar estos elementos cuando el usuario hace clic fuera del área de menús.
¿Es posible algo así con jQuery?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
javascript
jquery
click
Sergio del Amo
fuente
fuente
event.target
y sinevent.stopPropagation
.Respuestas:
Adjunte un evento de clic al cuerpo del documento que cierra la ventana. Adjunte un evento de clic separado al contenedor que detiene la propagación al cuerpo del documento.
fuente
$('html').click()
cuerpo. El cuerpo siempre tiene la altura de su contenido. Si no hay mucho contenido o la pantalla es muy alta, solo funciona en la parte que llena el cuerpo.Puede escuchar un evento de clic
document
y luego asegurarse de que#menucontainer
no sea un antepasado o el objetivo del elemento cliqueado mediante.closest()
.Si no es así, el elemento en el que se hizo clic está fuera del
#menucontainer
y puede ocultarlo de forma segura.Editar - 2017-06-23
También puede limpiar después del escucha de eventos si planea cerrar el menú y desea dejar de escuchar eventos. Esta función limpiará solo el oyente recién creado, preservando cualquier otro oyente de clics
document
. Con la sintaxis ES2015:Editar - 2018-03-11
Para aquellos que no quieren usar jQuery. Aquí está el código anterior en vanillaJS simple (ECMAScript6).
NOTA: Esto se basa en el comentario de Alex para usarlo en
!element.contains(event.target)
lugar de la parte jQuery.Pero
element.closest()
ahora también está disponible en todos los principales navegadores (la versión W3C difiere un poco de la versión jQuery). Polyfills se puede encontrar aquí: Element.closest ()Editar - 2020-05-21
En el caso en que desee que el usuario pueda hacer clic y arrastrar dentro del elemento, luego suelte el mouse fuera del elemento, sin cerrar el elemento:
Y en
outsideClickListener
:fuente
!element.contains(event.target)
usando Node.contains ()La razón por la que esta pregunta es tan popular y tiene tantas respuestas es porque es engañosamente compleja. Después de casi ocho años y docenas de respuestas, estoy realmente sorprendido de ver cuán poco se ha prestado atención a la accesibilidad.
Esta es una causa noble y es el problema real . El título de la pregunta, que es lo que la mayoría de las respuestas parecen intentar abordar, contiene un desafortunado arenque rojo.
Sugerencia: es la palabra "clic" !
En realidad, no desea vincular los controladores de clics.
Si vincula los controladores de clic para cerrar el cuadro de diálogo, ya ha fallado. La razón por la que ha fallado es que no todos desencadenan
click
eventos. Los usuarios que no usen un mouse podrán escapar de su diálogo (y su menú emergente podría decirse que es un tipo de diálogo) presionando Tab, y luego no podrán leer el contenido detrás del diálogo sin activar posteriormente unclick
evento.Así que reformulemos la pregunta.
Este es el objetivo. Desafortunadamente, ahora necesitamos vincular el
userisfinishedwiththedialog
evento, y esa vinculación no es tan sencilla.Entonces, ¿cómo podemos detectar que un usuario ha terminado de usar un diálogo?
focusout
eventoUn buen comienzo es determinar si el foco ha salido del diálogo.
Sugerencia: ¡tenga cuidado con el
blur
evento,blur
no se propaga si el evento estaba vinculado a la fase de burbujeo!jQuery
focusout
lo hará bien. Si no puede usar jQuery, puede usarloblur
durante la fase de captura:Además, para muchos cuadros de diálogo deberá permitir que el contenedor se enfoque. Agregar
tabindex="-1"
para permitir que el cuadro de diálogo reciba el foco dinámicamente sin interrumpir el flujo de tabulación.Si juegas con esa demo por más de un minuto, deberías comenzar a ver rápidamente los problemas.
El primero es que no se puede hacer clic en el enlace del cuadro de diálogo. Si intentas hacer clic en él o presionarlo, se cerrará el cuadro de diálogo antes de que tenga lugar la interacción. Esto se debe a que enfocar el elemento interno desencadena un
focusout
evento antes de activarlofocusin
nuevamente.La solución es poner en cola el cambio de estado en el bucle de eventos. Esto se puede hacer usando
setImmediate(...)
, osetTimeout(..., 0)
para navegadores que no son compatiblessetImmediate
. Una vez en cola, puede ser cancelado por un posteriorfocusin
:Mostrar fragmento de código
El segundo problema es que el diálogo no se cerrará cuando se presione nuevamente el enlace. Esto se debe a que el cuadro de diálogo pierde el foco, desencadenando el comportamiento de cierre, después de lo cual el clic en el enlace hace que el cuadro de diálogo se vuelva a abrir.
Similar al problema anterior, el estado de enfoque necesita ser administrado. Dado que el cambio de estado ya se ha puesto en cola, solo es cuestión de manejar eventos de enfoque en los disparadores de diálogo:
Esto debería parecer familiarMostrar fragmento de código
Esc llave
Si creía que había terminado manejando los estados de enfoque, hay más que puede hacer para simplificar la experiencia del usuario.
Esto es a menudo una característica "agradable de tener", pero es común que cuando tenga un modo o ventana emergente de cualquier tipo, la Escclave la cierre.
Mostrar fragmento de código
Si sabe que tiene elementos enfocables dentro del diálogo, no necesitará enfocar el diálogo directamente. Si está creando un menú, podría enfocar el primer elemento del menú.
Mostrar fragmento de código
Roles WAI-ARIA y otro soporte de accesibilidad
Es de esperar que esta respuesta cubra los aspectos básicos del soporte accesible de teclado y mouse para esta función, pero como ya es bastante considerable, voy a evitar cualquier discusión sobre los roles y atributos de WAI-ARIA , sin embargo, recomiendo encarecidamente que los implementadores consulten la especificación para obtener más detalles. sobre qué roles deben usar y cualquier otro atributo apropiado.
fuente
Las otras soluciones aquí no funcionaron para mí, así que tuve que usar:
fuente
&& !$(event.target).parents("#foo").is("#foo")
dentro de laIF
declaración para que los elementos secundarios no cierren el menú al hacer clic..is('#foo, #foo *')
, pero no recomiendo vincular manejadores de clics para resolver este problema .!$(event.target).closest("#foo").length
sería mejor y obvia la necesidad de agregar a @ honyovk.Tengo una aplicación que funciona de manera similar al ejemplo de Eran, excepto que adjunto el evento click al cuerpo cuando abro el menú ... Algo así:
Más información sobre la
one()
función de jQueryfuente
.one
- dentro del$('html')
controlador - escriba a$('html').off('click')
.one
controlador llamará automáticamenteoff
(como se muestra en los documentos de jQuery).Después de la investigación, he encontrado tres soluciones de trabajo (olvidé los enlaces de la página como referencia)
Primera solución
Segunda solución
Tercera solución
fuente
document.getElementsByClassName
, si alguien tiene una pista, por favor comparta.A mí me funciona bien.
fuente
#menucontainer
pregunta sobre un clictabindex="-1"
al#menuscontainer
para que funcione. Parece que si coloca una etiqueta de entrada dentro del contenedor y hace clic en él, el contenedor se oculta.Ahora hay un complemento para eso: eventos externos ( publicación de blog )
El siguiente sucede cuando un clickoutside controlador (WLOG) está unido a un elemento:
Por lo tanto, no se detiene la propagación de eventos y se pueden usar controladores de clic adicionales "encima" del elemento con el controlador externo.
fuente
$( '#element' ).on( 'clickoutside', function( e ) { .. } );
¡Esto funcionó para mí perfectamente!
fuente
No creo que lo que realmente necesita es cerrar el menú cuando el usuario hace clic afuera; lo que necesita es que el menú se cierre cuando el usuario haga clic en cualquier lugar de la página. Si hace clic en el menú o fuera del menú, ¿debería cerrarse?
Al no encontrar respuestas satisfactorias arriba me impulsó a escribir esta publicación de blog el otro día. Para los más pedantes, hay una serie de problemas para tener en cuenta:
body { margin-left:auto; margin-right: auto; width:960px;}
fuente
Como dijo otro cartel, hay muchas trampas, especialmente si el elemento que está mostrando (en este caso, un menú) tiene elementos interactivos. He encontrado que el siguiente método es bastante robusto:
fuente
Una solución simple para la situación es:
El script anterior ocultará
div
sidiv
se activa fuera del evento click.Puede ver el siguiente blog para obtener más información: http://www.codecanal.com/detect-click-outside-div-using-javascript/
fuente
Solución1
En lugar de usar event.stopPropagation () que puede tener algunos efectos secundarios, solo defina una variable de bandera simple y agregue una
if
condición. Probé esto y trabajé correctamente sin ningún efecto secundario de stopPropagation:Solution2
Con solo una
if
condición simple :fuente
Verifique el destino del evento de clic de la ventana (debe propagarse a la ventana, siempre que no se capture en ningún otro lugar) y asegúrese de que no sea ninguno de los elementos del menú. Si no es así, entonces estás fuera de tu menú.
O verifique la posición del clic y vea si está dentro del área del menú.
fuente
He tenido éxito con algo como esto:
La lógica es: cuando
#menuscontainer
se muestra, enlace un controlador de clic al cuerpo que se oculta#menuscontainer
solo si el objetivo (del clic) no es hijo de él.fuente
Como variante:
No tiene ningún problema para detener la propagación de eventos y admite mejor varios menús en la misma página donde al hacer clic en un segundo menú mientras está abierto uno primero, dejará el primero abierto en la solución stopPropagation.
fuente
El evento tiene una propiedad llamada event.path del elemento que es una "lista ordenada estática de todos sus antepasados en orden de árbol" . Para verificar si un evento se originó a partir de un elemento DOM específico o uno de sus elementos secundarios, simplemente verifique la ruta para ese elemento DOM específico. También se puede usar para verificar múltiples elementos al lógicamente
OR
la verificación del elemento en lasome
función.Entonces, para su caso, debería ser
fuente
event.path
no es una cosaEncontré este método en algún complemento de calendario jQuery.
fuente
Aquí está la solución JavaScript vainilla para futuros espectadores.
Al hacer clic en cualquier elemento del documento, si la identificación del elemento seleccionado se alterna o si el elemento oculto no está oculto y el elemento oculto no contiene el elemento seleccionado, alterne el elemento.
Mostrar fragmento de código
Si va a tener varios conmutadores en la misma página, puede usar algo como esto:
hidden
al elemento plegable.fuente
Me sorprende que nadie haya reconocido el
focusout
evento:fuente
Si está creando scripts para IE y FF 3. * y solo desea saber si el clic se produjo dentro de un área de cuadro determinada, también podría usar algo como:
fuente
En lugar de utilizar la interrupción de flujo, el evento de desenfoque / enfoque o cualquier otra técnica complicada, simplemente combine el flujo de eventos con el parentesco del elemento:
Para eliminar haga clic fuera del detector de eventos, simplemente:
fuente
#menuscontainer
todavía estás pasando por sus padres. primero debes verificar eso, y si no es ese elemento, sube el árbol DOM.if(!($(event.target).is("#menuscontainer") || $(event.target).parents().is("#menuscontainer"))){
. Es una pequeña optimización, pero ocurre solo unas pocas veces en la vida de su programa: por cada clic, si elclick.menu-outside
evento está registrado. Es más largo (+32 caracteres) y no utiliza el método de encadenamientoUtilizar:
fuente
Si alguien curioso aquí es la solución de JavaScript (es6):
y es5, por si acaso:
});
fuente
Aquí hay una solución simple de javascript puro. Está actualizado con ES6 :
fuente
() => {}
lugar de hacerlofunction() {}
. Lo que tienes allí está clasificado como JavaScript simple con un toque de ES6.Enganche un detector de eventos de clic en el documento. Dentro del detector de eventos, puede mirar el objeto del evento , en particular, el event.target para ver en qué elemento se hizo clic:
fuente
He usado el siguiente script y hecho con jQuery.
A continuación encuentre el código HTML
Puedes leer el tutorial aquí
fuente
Si hace clic en el documento, oculte un elemento dado, a menos que haga clic en ese mismo elemento.
fuente
Vota por la respuesta más popular, pero agrega
entonces, un clic en una barra de desplazamiento no [oculta o lo que sea] su elemento objetivo.
fuente
Para un uso más fácil y un código más expresivo, creé un complemento jQuery para esto:
Nota: target es el elemento en el que el usuario hizo clic. Pero la devolución de llamada todavía se ejecuta en el contexto del elemento original, por lo que puede utilizar esto como esperaría en una devolución de llamada jQuery.
Enchufar:
De forma predeterminada, el detector de eventos de clic se coloca en el documento. Sin embargo, si desea limitar el alcance del detector de eventos, puede pasar un objeto jQuery que represente un elemento de nivel principal que será el principal superior en el que se escucharán los clics. Esto evita escuchas de eventos a nivel de documento innecesarios. Obviamente, no funcionará a menos que el elemento principal proporcionado sea un elemento primario de su elemento inicial.
Use así:
fuente