Es una cosa razonable de UX que puede hacer clic para abrir algo, y luego no solo poder hacer clic en lo mismo para cerrarlo, sino también hacer clic fuera de lo que abrió para cerrarlo. Kitty Giraudel acaba de bloguear sobre eso. El truco es que una vez que se abre la cosa, adjunta un controlador de eventos a la window
con eso relojes para eventos (como otro clic). Si ese clic posterior no se produjo dentro del área recién abierta, ciérrela. como, literalmente thing.contains(event.target)
. Es un buen truco, creo.
Sin embargo, hay muchas pequeñas cosas en las que pensar. Por ejemplo:
tenemos que detener la propagación del evento de clic en el interruptor mismo. De lo contrario, sube a la ventana del oyente de clics, y dado que la palanca no está contenida dentro del menú, cerraría este último tan pronto como intentemos abrirlo.
Bien bien. No se puede tener eso o se rompe todo.
Tenemos este mismo patrón en muchos lugares en CodePen. Al igual que Kitty, lo tenemos implementado en React. Al echar un vistazo a nuestra implementación, hay una serie de campanas y silbatos que pensé que valía la pena mencionar. Por ejemplo, el nuestro no es una función o gancho, sino un contenedor de componentes que usamos como:
<ClickOutsideDetector
listen
onClickOutside={() => { closeTheThing(); }}
>
A Menu or Modal or something.
</ClickOutsideDetector>
De esa manera es un contenedor genérico que podemos usar para cualquier cosa en un “clic afuera”. Las campanas y silbatos son:
- puedes pasar
component
prop para que no tenga que manifestarse como un<div>
pero lo que quieras que sea semánticamente. - El
listen
prop le permite alternar si actualmente está escuchando activamente eventos. Como una forma rápida de cortocircuitarlo. - Una pulsación de tecla ESC es el equivalente funcional a hacer clic fuera.
- Maneja eventos táctiles y clics
- Maneja un caso en el que el clic exterior pasa a un
<iframe>
en cuyo caso elwindow
tiene unblur
evento en lugar de un clic. - Le permite pasar elementos para ignorar, por lo que en lugar de
stopPropagation
truco que Kitty documentó, podemos ser específicos acerca de los elementos que no activan un clic en el exterior.
¡Tantas cositas! Para mí, este es el pequeño ejemplo perfecto del desarrollo del mundo real. Solo desea un pequeño comportamiento y, en última instancia, hay un montón de consideraciones y casos extremos con los que tiene que lidiar y nunca se hace realmente. Acabo de tocar nuestro componente en los últimos meses debido a que una herramienta de terceros que usamos cambió la forma en que hicieron algo que afectó a los iframes en uso en la página. En última instancia, tuve que estar atento a un blur
evento que luego verifique el classList
de document.activeElement
a ver si eso era lo que se estaba comiendo el click de afuera!
Annnnyway, lancé una versión nuestra solo un poco simplificada aquí.
Y vi algo de la publicación de Kitty que no estábamos manejando, y está en la primera oración:
necesitábamos una forma de cerrar el menú al hacer clic fuera de él o tabulando fuera de él.
Énfasis mío. No se preocupe, tengo un TODO en nuestro código para eso ahora.