¿Cómo determinar el clic fuera del elemento?

Tengo algunos menús HTML que muestro en su totalidad cuando el usuario hace clic en el título de estos menús. Me gustaría ocultar estos elementos cuando el usuario haga clic fuera del área del menú.

¿Es algo como esto posible con jQuery?

 $("#menuscontainer").clickOutsideThisElement(function() { // Hide the menus }); 
2160
30 сент. conjunto de Sergio del Amo 30 sep. 2008-09-30 16:17 '08 a las 4:17 pm 2008-09-30 16:17
@ 78 respuestas
  • 1
  • 2
  • 3

NOTA. El uso de stopEventPropagation() debe evitarse ya que interrumpe el flujo normal de eventos en el DOM. Vea este artículo para más información. Trate de usar este método en su lugar.

Adjunte un evento de clic al cuerpo del documento que cierra la ventana. Adjunte un evento de clic separado a un contenedor que detiene la distribución en el cuerpo del documento.

border=0
 $(window).click(function() { //Hide the menus if visible }); $('#menucontainer').click(function(event){ event.stopPropagation(); }); 
1671
30 сент. La respuesta la da Eran Galperin el 30 de septiembre. 2008-09-30 16:38 '08 a las 4:38 pm 2008-09-30 16:38

Puede escuchar el evento de clic para el document y luego asegurarse de que #menucontainer no sea el antecesor o destino del elemento .closest() con el .closest() .

Si este no es el caso, entonces el elemento #menucontainer está fuera del #menucontainer y puede ocultarse de manera segura.

 export function hideOnClickOutside(selector) { const outsideClickListener = (event) => { $target = $(event.target); if (!$target.closest(selector).length  $(selector).is(':visible')) { $(selector).hide(); removeClickListener(); } } const removeClickListener = () => { document.removeEventListener('click', outsideClickListener) } document.addEventListener('click', outsideClickListener) } 
border=0

Editar - 2018-03-11

Para aquellos que no quieren usar jQuery. Aquí está el código de arriba en vanillaJS (ECMAScript6).

1212
12 июня '10 в 11:35 2010-06-12 11:35 La respuesta es dada Art 12 June '10 at 11:35 2010-06-12 11:35

¿Cómo detectar click fuera del elemento?

La razón por la que esta pregunta es tan popular y tiene tantas respuestas es que es engañosamente compleja. Después de casi ocho años y docenas de respuestas, estoy realmente sorprendido de ver cuánta atención se presta a la accesibilidad.

Me gustaría ocultar estos elementos cuando el usuario haga clic fuera del área del menú.

Esta es una causa noble y es un problema real. El nombre de la pregunta es lo que la mayoría de las respuestas parecen estar intentando resolver, contiene una decepcionante pista falsa.

Sugerencia: esta palabra es "clic"!

De hecho, no desea enlazar los controladores de clics.

Si enlaza los controladores de clic para cerrar el cuadro de diálogo, ya ha fallado. La razón por la que falló es que no todos activan los eventos de click . Los usuarios que no usan el mouse podrán salir de su cuadro de diálogo (y su menú emergente puede ser un tipo de diálogo) presionando la tecla Tab , y luego no podrán leer el contenido detrás del diálogo sin iniciar un click .

Así que vamos a reformular la pregunta.

¿Cómo cerrar el diálogo cuando el usuario haya terminado con él?

Este es el objetivo. Desafortunadamente, ahora debemos vincular al userisfinishedwiththedialog evento de userisfinishedwiththedialog , y este enlace no es tan simple.

Entonces, ¿cómo podemos descubrir que un usuario ha terminado de usar el diálogo?

evento de focusout

Un buen comienzo es determinar si el foco ha abandonado el diálogo.

Consejo: tenga cuidado con el blur del evento, el blur no se aplica si el evento se asoció con la fase de propagación.

jQuery focusout será genial. Si no puede usar jQuery, puede usar blur en la fase de captura:

 element.addEventListener('blur', ..., true); // use capture: ^^^^ 

Además, para muchos cuadros de diálogo, deberá permitir que el contenedor se enfoque. Agregue tabindex="-1" para que el cuadro de diálogo pueda recibir un enfoque dinámico sin interrumpir el flujo de pestañas.

 div { display: none; } .active { display: block; } 
Example <div id="example" tabindex="-1"> Lorem ipsum <a href="http://example.com">dolor sit amet. </div> 

Si juegas con esta demostración por más de un minuto, deberías comenzar a ver los problemas rápidamente.

Primero, el enlace en el cuadro de diálogo no está disponible. Si intenta hacer clic en él o en una pestaña, se cerrará el cuadro de diálogo antes de que ocurra la interacción. Esto se debe al hecho de que enfocar el elemento interno provoca un evento de focusout antes de focusin evento de focusin .

La corrección es una cola de estado en el bucle de eventos. Esto se puede hacer usando setImmediate(...) o setTimeout(..., 0) para los navegadores que no admiten setImmediate . Una vez en cola, puede ser cancelado por un focusin subsiguiente:

 $('.submenu').on({ focusout: function (e) { $(this).data('submenuTimer', setTimeout(function () { $(this).removeClass('submenu--active'); }.bind(this), 0)); }, focusin: function (e) { clearTimeout($(this).data('submenuTimer')); } }); 

 div { display: none; } .active { display: block; } 
Example <div id="example" tabindex="-1"> Lorem ipsum <a href="http://example.com">dolor sit amet. </div> 

El segundo problema es que el diálogo no se cerrará cuando vuelva a hacer clic en el enlace. Esto se debe al hecho de que el diálogo pierde el foco, lo que provoca un desencadenante al cerrarse, después de lo cual al hacer clic en el enlace se abre el diálogo.

Como en la versión anterior, necesita controlar el estado de enfoque. Teniendo en cuenta que el cambio de estado ya se ha puesto en cola, es solo una cuestión de procesar eventos de enfoque en un modo interactivo:

Debe parecer familiar.
 $('a').on({ focusout: function () { $(this.hash).data('timer', setTimeout(function () { $(this.hash).removeClass('active'); }.bind(this), 0)); }, focusin: function () { clearTimeout($(this.hash).data('timer')); } }); 

 div { display: none; } .active { display: block; } 
Example <div id="example" tabindex="-1"> Lorem ipsum <a href="http://example.com">dolor sit amet. </div> 

Tecla esc

Si cree que todo se hace gestionando los estados de enfoque, puede hacer más para simplificar la experiencia del usuario.

A menudo, esto es "agradable de tener", pero generalmente sucede que cuando tiene un archivo modal o emergente de cualquier tipo que lo cierra con la tecla Esc .

 keydown: function (e) { if (e.which === 27) { $(this).removeClass('active'); e.preventDefault(); } } 

 div { display: none; } .active { display: block; } 
Example <div id="example" tabindex="-1"> Lorem ipsum <a href="http://example.com">dolor sit amet. </div> 

Si sabe que hay elementos personalizados en el cuadro de diálogo, no necesita enfocar directamente el diálogo. Si creas un menú, puedes enfocar el primer elemento del menú.

 click: function (e) { $(this.hash) .toggleClass('submenu--active') .find('a:first') .focus(); e.preventDefault(); } 

 .menu { list-style: none; margin: 0; padding: 0; } .menu:after { clear: both; content: ''; display: table; } .menu__item { float: left; position: relative; } .menu__link { background-color: lightblue; color: black; display: block; padding: 0.5em 1em; text-decoration: none; } .menu__link:hover, .menu__link:focus { background-color: black; color: lightblue; } .submenu { border: 1px solid black; display: none; left: 0; list-style: none; margin: 0; padding: 0; position: absolute; top: 100%; } .submenu--active { display: block; } .submenu__item { width: 150px; } .submenu__link { background-color: lightblue; color: black; display: block; padding: 0.5em 1em; text-decoration: none; } .submenu__link:hover, .submenu__link:focus { background-color: black; color: lightblue; } 
Menu 1</a> <ul class="submenu" id="menu-1" tabindex="-1"> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li> </ul> </li> <li class="menu__item"> <a class="menu__link" href="#menu-2">Menu 2</a> <ul class="submenu" id="menu-2" tabindex="-1"> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li> </ul> </li> </ul> lorem ipsum <a href="http://example.com/">dolor sit amet. 

Roles WAI-ARIA y otro soporte de accesibilidad

Espero que esta respuesta cubra los aspectos básicos del teclado y el mouse disponibles para esta función, pero como ya es bastante importante, evitaré discutir los roles y atributos de WAI-ARIA , pero recomiendo encarecidamente a los desarrolladores que consulten las especificaciones para obtener más detalles. los roles que deben usar, y cualquier otro atributo relevante.

220
12 июля '16 в 2:29 2016-07-12 02:29 la respuesta se da zzzzBov 12 de julio de '16 a las 2:29 a.m. 2016-07-12 02:29

Otras soluciones aquí no funcionaron para mí, así que tuve que usar:

 if(!$(event.target).is('#foo')) { // hide menu } 
132
07 июля '09 в 2:10 2009-07-07 02:10 La respuesta se le da a Dennis el 7 de julio de 2009 a las 2:10 2009-07-07 02:10

Tengo una aplicación que funciona de manera similar al ejemplo de Eran, excepto que adjunto un evento de clic al cuerpo al abrir el menú ... Algo así:

 $('#menucontainer').click(function(event) { $('html').one('click',function() { // Hide the menus }); event.stopPropagation(); }); 

Más información sobre la función jQuery one() .

122
30 сент. Respuesta dada por Joe Lencioni el 30 de septiembre. 2008-09-30 21:13 '08 a las 9:13 pm 2008-09-30 21:13
 $("#menuscontainer").click(function() { $(this).focus(); }); $("#menuscontainer").blur(function(){ $(this).hide(); }); 

Funciona para mí muy bien.

36
17 нояб. La respuesta es dada por user212621 17 de noviembre. 2009-11-17 09:13 '09 a las 9:13 2009-11-17 09:13

Ahora hay un complemento para esto: eventos externos ( publicación de blog )

Lo siguiente ocurre cuando un controlador de clickoutside (WLOG) está vinculado a un elemento:

  • un elemento se agrega a una matriz que contiene todos los elementos con controladores de clickoutside.
  • un controlador de clic ( espacio de nombre ) está vinculado al documento (si aún no existe)
  • en cualquier clic en el documento, el evento clickoutside se activa para aquellos elementos en esta matriz que no son iguales o para el objeto principal del clic de destino
  • Además, el evento.target para el evento clickoutside se establece en el elemento en el que el usuario hizo clic (por lo que incluso sabe que el usuario hizo clic, y no solo que hizo clic fuera).

Por lo tanto, ningún evento finaliza debido a la propagación, y los controladores de clics adicionales se pueden utilizar "arriba" con la ayuda de un controlador externo.

35
05 апр. La respuesta es dada por Wolfram 05 Abr. 2010-04-05 13:07 '10 a las 13:07 2010-04-05 13:07

Después de investigar, encontré tres soluciones de trabajo (olvidé los enlaces a las páginas de referencia)

Primera decision

 <script> //The good thing about this solution is it doesn't stop event propagation. var clickFlag = 0; $('body').on('click', function () { if(clickFlag == 0) { console.log('hide element here');  } else { clickFlag=0; } }); $('body').on('click','#testDiv', function (event) { clickFlag = 1; console.log('showed the element');  }); </script> 

Segunda solucion

 <script> $('body').on('click', function(e) { if($(e.target).closest('#testDiv').length == 0) {  } }); </script> 

Tercera solucion

 <script> var specifiedElement = document.getElementById('testDiv'); document.addEventListener('click', function(event) { var isClickInside = specifiedElement.contains(event.target); if (isClickInside) { console.log('You clicked inside') } else { console.log('You clicked outside') } }); </script> 
32
02 нояб. La respuesta la da Rameez Rami 02 nov. 2015-11-02 11:33 '15 a las 11:33 2015-11-02 11:33

Funcionó muy bien para mí!

 $('html').click(function (e) { if (e.target.id == 'YOUR-DIV-ID') { //do something } else { //do something } }); 
29
04 июня '12 в 17:08 2012-06-04 17:08 la respuesta se da srinath 04 junio '12 a las 17:08 2012-06-04 17:08

No creo que realmente necesite cerrar el menú cuando el usuario lo haga clic; necesita que el menú se cierre cuando el usuario haga clic en cualquier lugar de la página. Si hace clic en un menú o en un menú externo, ¿debería cerrarlo correctamente?

No encontrando respuestas satisfactorias, pedí escribir este blog el otro día. Para los más pedantes, hay algunos errores a tomar en cuenta:

  • Si adjunta un controlador de eventos de clic al elemento del cuerpo durante un clic, asegúrese de esperar un segundo clic antes de cerrar el menú y desatar el evento. De lo contrario, el evento de clic que abrió el menú aparecerá en la pantalla del oyente, quien debería cerrar el menú.
  • Si usa event.stopPropogation () en un evento de clic, ningún otro elemento de su página puede tener la función de hacer clic en dónde cerrar.
  • Adjuntar un controlador de eventos de clic a un elemento del cuerpo es ilimitado no es una solución efectiva.
  • Al comparar el propósito del evento y sus padres con el creador del controlador, se supone que desea cerrar el menú al hacer clic en él, cuando lo que realmente desea es cerrarlo al hacer clic en cualquier lugar de la página.
  • Escuchar eventos en el elemento del cuerpo hará que su código sea más frágil. El estilo es inocente, ya que podría romperlo: body { margin-left:auto; margin-right: auto; width:960px;} body { margin-left:auto; margin-right: auto; width:960px;}
24
19 мая '11 в 0:15 2011-05-19 00:15 la respuesta se da 34m0 19 de mayo, '11 a las 0:15 2011-05-19 00:15

Como otro póster, se dice que hay muchos errores, especialmente si el elemento que está mostrando (en este caso, el menú) tiene elementos interactivos. He encontrado el siguiente método bastante confiable:

 $('#menuscontainer').click(function(event) { //your code that shows the menus fully //now set up an event listener so that clicking anywhere outside will close the menu $('html').click(function(event) { //check up the tree of the click target to check whether user has clicked outside of menu if ($(event.target).parents('#menuscontainer').length==0) { // your code to hide menu //this event listener has done its job so we can unbind it. $(this).unbind(event); } }) }); 
23
22 дек. respuesta dada por benb el 22 de diciembre 2011-12-22 14:59 '11 a las 14:59 2011-12-22 14:59

Una solución simple a la situación es:

 $(document).mouseup(function (e) { var container = $("YOUR SELECTOR"); // Give you class or ID if (!container.is(e.target)  // If the target of the click is not the desired div or section container.has(e.target).length === 0) // ... nor a descendant-child of the container { container.hide(); } }); 

La secuencia de comandos anterior ocultará el div si se activa el evento externo div click.

Para obtener más información, consulte el siguiente blog: http://www.codecanal.com/detect-click-outside-div-using-javascript/

20
15 дек. La respuesta la da Jitendra Damor el 15 de diciembre. 2015-12-15 06:50 '15 a las 6:50 2015-12-15 06:50

Verifique el punto de destino del evento de clic de la ventana (debe distribuirse en la ventana si no se ha capturado en algún lugar) y asegúrese de que no sea uno de los elementos del menú. Si no, estás fuera de tu menú.

O verifique la posición del clic y vea si está en el área del menú.

18
30 сент. Respuesta dada por Chris MacDonald el 30 de septiembre. 2008-09-30 16:20 '08 a las 4:20 pm 2008-09-30 16:20

Solución 1

En lugar de usar event.stopPropagation (), que puede tener algunos efectos secundarios, simplemente defina una variable de marca simple y agregue una condición if . Lo probé y funcioné bien sin ningún efecto secundario de stopPropagation:

 var flag = "1"; $('#menucontainer').click(function(event){ flag = "0"; // flag 0 means click happened in the area where we should not do any action }); $('html').click(function() { if(flag != "0"){ // Hide the menus if visible } else { flag = "1"; } }); 

Solución 2

Usando una simple condición if :

 $(document).on('click', function(event){ var container = $("#menucontainer"); if (!container.is(event.target)  // If the target of the click isn't the container... container.has(event.target).length === 0) // ... nor a descendant of the container { // Do whatever you want to do when click is outside the element } }); 
18
28 янв. Responder Iman Sedighi 28 de enero 2015-01-28 20:24 '15 a las 20:24 2015-01-28 20:24

Tuve éxito con algo como esto:

 var $menuscontainer = ...; $('#trigger').click(function() { $menuscontainer.show(); $('body').click(function(event) { var $target = $(event.target); if ($target.parents('#menuscontainer').length == 0) { $menuscontainer.hide(); } }); }); 

La lógica es la siguiente: cuando #menuscontainer muestra #menuscontainer , #menuscontainer el controlador de clic a un cuerpo que oculte #menuscontainer solo si el objetivo (clic) no es un elemento secundario.

15
02 дек. La respuesta la da Chu Yeow 02 dic. 2010-12-02 12:53 '10 a las 12:53 2010-12-02 12:53

Como opción:

 var $menu = $('#menucontainer'); $(document).on('click', function (e) { // If element is opened and click target is outside it, hide it if ($menu.is(':visible')  !$menu.is(e.target)  !$menu.has(e.target).length) { $menu.hide(); } }); 

No tiene problemas para detener la distribución de eventos y mantiene mejor varios menús en la misma página, donde hacer clic en el segundo menú cuando lo abre por primera vez dejará el primer descubrimiento en la solución stopPropagation.

14
24 июля '14 в 12:41 2014-07-24 12:41 Bohdan Lyzanets dio respuesta el 24 de julio de 2014 a las 12:41 2014-07-24 12:41

Encontré este método en algún complemento de calendario de jQuery.

 function ClickOutsideCheck(e) { var el = e.target; var popup = $('.popup:visible')[0]; if (popup==undefined) return true; while (true){ if (el == popup ) { return true; } else if (el == document) { $(".popup").hide(); return false; } else { el = $(el).parent()[0]; } } }; $(document).bind('mousedown.popup', ClickOutsideCheck); 
12
28 июля '11 в 17:06 2011-07-28 17:06 la respuesta la da nazar kuliyev el 28 de julio de 2011 a las 17:06 2011-07-28 17:06

Aquí está la solución para javascript de vainilla para futuros espectadores.

Al hacer clic en cualquier elemento dentro del documento, si se hace clic en el identificador del elemento pulsado o si el elemento oculto no está oculto, y el elemento oculto no contiene un elemento con un clic, cambie el elemento.

 (function () { "use strict"; var hidden = document.getElementById('hidden'); document.addEventListener('click', function (e) { if (e.target.id == 'toggle' || (hidden.style.display != 'none'  !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none'; }, false); })(); 

Si va a tener varios interruptores en la misma página, puede usar algo como esto:

  • Agregue el nombre de la clase hidden al elemento de plegado.
  • Al hacer clic en un documento, cierre todos los elementos ocultos que no contienen un elemento con un clic y no están ocultos.
  • Si el elemento pulsado es un interruptor, cambie el elemento especificado.

10
20 мая '15 в 1:52 2015-05-20 01:52 La respuesta se le da a Tiny Giant el 20 de mayo de 2015 a las 1:52 2015-05-20 01:52

Un evento tiene una propiedad llamada event.path del elemento, que es una "lista estática ordenada de todos sus ancestros en orden de árbol". Para verificar si un evento ha ocurrido desde un elemento DOM específico o uno de sus elementos secundarios, simplemente verifique la ruta a ese elemento DOM particular. También se puede utilizar para verificar varios elementos de un OR lógico cuando se verifica un elemento en some función.

 #main { display: inline-block; } 
var _tmr = window._tmr || (window._tmr = []);_tmr.push({id: "2334768", type: "pageView", start: (new Date()).getTime()});(function (d, w, id) {  if (d.getElementById(id)) return;  var ts = d.createElement("script"); ts.type = "text/javascript"; ts.async = true; ts.id = id;  ts.src = (d.location.protocol == "https:" ? "https:" : "http:") + "//top-fwz1.mail.ru/js/code.js";  var f = function () {var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ts, s);};  if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); }})(document, window, "topmailru-code");