Estoy buscando una manera de detectar si ocurrió un evento de clic fuera de un componente, como se describe en este artículo . jQueryarest () se usa para ver si el objetivo de un evento de clic tiene el elemento dom como uno de sus padres. Si hay una coincidencia, el evento de clic pertenece a uno de los elementos secundarios y, por lo tanto, no se considera que esté fuera del componente.
Entonces, en mi componente, quiero adjuntar un controlador de clic a la ventana. Cuando se dispara el controlador, necesito comparar el objetivo con los hijos de dom de mi componente.
El evento de clic contiene propiedades como "ruta" que parece contener la ruta dom que ha recorrido el evento. No estoy seguro de qué comparar o cómo recorrerlo mejor, y creo que alguien ya debe haber puesto eso en una función de utilidad inteligente ... ¿No?
fuente
Respuestas:
El uso de referencias en React 16.3+ cambió.
La siguiente solución utiliza ES6 y sigue las mejores prácticas para vincular y configurar la referencia mediante un método.
Para verlo en acción:
Implementación de clase:
Implementación de ganchos:
fuente
document.addEventListener
aquí?context
solo es necesario como argumento en constructor si se usa. No se está utilizando en este caso. La razón por la cual el equipo de reacción recomienda tenerprops
un argumento en el constructor es porque el uso dethis.props
antes de llamarsuper(props)
no estará definido y puede provocar errores.context
todavía está disponible en los nodos internos, ese es el propósito decontext
. De esta manera, no tiene que pasarlo de un componente a otro como lo hace con los accesorios. 2. Esto es solo una preferencia estilística, y no garantiza debatir en este caso.Aquí está la solución que mejor funcionó para mí sin adjuntar eventos al contenedor:
Ciertos elementos HTML pueden tener lo que se conoce como " foco ", por ejemplo, elementos de entrada. Esos elementos también responderán al evento de desenfoque , cuando pierdan ese enfoque.
Para dar a cualquier elemento la capacidad de tener foco, solo asegúrese de que su atributo tabindex esté configurado en algo que no sea -1. En HTML normal, eso sería mediante la configuración del
tabindex
atributo, pero en React debe usartabIndex
(tenga en cuenta la mayúsculaI
).También puedes hacerlo a través de JavaScript con
element.setAttribute('tabindex',0)
Esto es para lo que lo estaba usando, para hacer un menú DropDown personalizado.
fuente
display:absolute
para que el menú desplegable no afecte el tamaño del div principal. Esto significa que cuando hago clic en un elemento en el menú desplegable, se activa el desenfoque.onBlur
se activarán en su contenedor desplegable yexpanded
se establecerán en falso.Estaba atrapado en el mismo problema. Llego un poco tarde a la fiesta aquí, pero para mí esta es una muy buena solución. Esperemos que sea de ayuda para alguien más. Necesitas importar
findDOMNode
desdereact-dom
Enfoque Reaccionar ganchos (16.8 +)
Puede crear un gancho reutilizable llamado
useComponentVisible
.Luego, en el componente, desea agregar la funcionalidad para hacer lo siguiente:
Encuentre un ejemplo de codeandbox aquí.
fuente
ReactDOM.findDOMNode
y está en desuso, debe usarref
devoluciones de llamada: github.com/yannickcr/eslint-plugin-react/issues/…Después de probar muchos métodos aquí, decidí usar github.com/Pomax/react-onclickoutside por lo completo que es.
Instalé el módulo a través de npm y lo importé a mi componente:
Luego, en mi clase de componente, definí el
handleClickOutside
método:Y al exportar mi componente lo envolví en
onClickOutside()
:Eso es.
fuente
tabIndex
/onBlur
enfoque propuesto por Pablo ? ¿Cómo funciona la implementación y cómo difiere su comportamiento delonBlur
enfoque?tabIndex/onBlur
enfoque. No funciona cuando el menú desplegable esposition:absolute
, como un menú que se cierne sobre otro contenido.Encontré una solución gracias a Ben Alpert en discusion.reactjs.org . El enfoque sugerido adjunta un controlador al documento, pero resultó ser problemático. Al hacer clic en uno de los componentes de mi árbol, se produjo una reproducción que eliminó el elemento seleccionado en la actualización. Debido a que la reproducción desde React ocurre antes de que se llame al controlador del cuerpo del documento, el elemento no se detectó como "dentro" del árbol.
La solución a esto fue agregar el controlador en el elemento raíz de la aplicación.
principal:
componente:
fuente
document
no está en uno ?mousedown
probablemente sería un mejor controlador queclick
aquí. En la mayoría de las aplicaciones, el comportamiento de cerrar el menú haciendo clic fuera ocurre en el momento en que presiona el mouse, no cuando lo suelta. Pruébelo, por ejemplo, con la bandera de Stack Overflow o comparta diálogos o con uno de los menús desplegables de la barra de menú superior de su navegador.ReactDOM.findDOMNode
y la cadenaref
están en desuso, deben usarref
devoluciones de llamada: github.com/yannickcr/eslint-plugin-react/issues/…document
Implementación de gancho basada en la excelente charla de Tanner Linsley en JSConf Hawaii 2020 :
useOuterClick
API de ganchoImplementación
useOuterClick
utiliza referencias mutables para crear una API magra paraClient
. Se puede configurar una devolución de llamada sin tener que memorizarla a través deuseCallback
. El cuerpo de devolución de llamada todavía tiene acceso a los accesorios y el estado más recientes (sin valores de cierre obsoletos).Nota para usuarios de iOS
iOS trata solo ciertos elementos como clicables. Para eludir este comportamiento, elija un oyente de clic externo diferente al que
document
no incluye nada hacia arribabody
. Por ejemplo, podría agregar un oyente en la raíz Reactdiv
en el ejemplo anterior y expandir su altura (height: 100vh
o similar) para capturar todos los clics externos. Fuentes: quirksmode.org y esta respuestafuente
@media (hover: none) and (pointer: coarse) { body { cursor:pointer } }
Agregando esto en mis estilos globales, parece que solucionó el problema.@supports (-webkit-overflow-scrolling: touch)
que se dirige solo a iOS, ¡aunque no sé cuánto! Lo acabo de probar y funciona.Ninguna de las otras respuestas aquí funcionó para mí. Estaba tratando de ocultar una ventana emergente en el desenfoque, pero como el contenido estaba en una posición absoluta, el onBlur también se activaba incluso con el clic de los contenidos internos.
Aquí hay un enfoque que funcionó para mí:
Espero que esto ayude.
fuente
document
. Gracias.[Actualización] Solución con React ^ 16.8 usando ganchos
CodeSandbox
Solución con React ^ 16.3 :
CodeSandbox
fuente
.contains is not a function
, puede deberse a que está pasando el accesorio de referencia a un componente personalizado en lugar de un elemento DOM real como un<div>
ref
utilería a un componente personalizado, es posible que quieran echar un vistazoReact.forwardRef
Aquí está mi enfoque (demo - https://jsfiddle.net/agymay93/4/ ):
He creado un componente especial llamado
WatchClickOutside
y se puede usar como (supongo que laJSX
sintaxis):Aquí está el código del
WatchClickOutside
componente:fuente
ref
está en desuso, debe usarref
devoluciones de llamada: reactjs.org/docs/refs-and-the-dom.htmlEsto ya tiene muchas respuestas, pero no abordan
e.stopPropagation()
y evitan hacer clic en los enlaces de reacción fuera del elemento que desea cerrar.Debido al hecho de que React tiene su propio controlador de eventos artificial, no puede usar el documento como base para los oyentes de eventos. Debe hacerlo
e.stopPropagation()
antes de esto, ya que React usa el documento en sí. Si usa por ejemplo en sudocument.querySelector('body')
lugar. Puede evitar el clic desde el enlace Reaccionar. El siguiente es un ejemplo de cómo implemento hacer clic afuera y cerrar.Esto usa ES6 y React 16.3 .
fuente
Para aquellos que necesitan un posicionamiento absoluto, una opción simple por la que opté es agregar un componente de envoltura diseñado para cubrir toda la página con un fondo transparente. Luego puede agregar un onClick en este elemento para cerrar su componente interno.
Como sucede ahora si agrega un controlador de clic en el contenido, el evento también se propagará al div superior y, por lo tanto, activará el controladorOutsideClick. Si este no es su comportamiento deseado, simplemente detenga la proyección del evento en su controlador.
``
fuente
Para ampliar la respuesta aceptada hecha por Ben Bud , si está utilizando componentes con estilo, pasar referencias de esa manera le dará un error como "this.wrapperRef.contains no es una función".
La solución sugerida, en los comentarios, para envolver el componente con estilo con un div y pasar la referencia allí, funciona. Dicho esto, en sus documentos ya explican la razón de esto y el uso adecuado de las referencias dentro de los componentes con estilo:
Al igual que:
Luego puede acceder directamente desde la función "handleClickOutside":
Esto también se aplica para el enfoque "onBlur":
fuente
Material-UI tiene un pequeño componente para resolver este problema: https://material-ui.com/components/click-away-listener/ que puede elegir. Pesa 1.5 kB comprimido, es compatible con dispositivos móviles, IE 11 y portales.
fuente
Mi mayor preocupación con todas las otras respuestas es tener que filtrar los eventos de clic desde la raíz / padre hacia abajo. Encontré que la forma más fácil era simplemente establecer un elemento hermano con posición: fijo, un índice z 1 detrás del menú desplegable y manejar el evento de clic en el elemento fijo dentro del mismo componente. Mantiene todo centralizado en un componente dado.
Código de ejemplo
fuente
El evento
path[0]
es el último elemento en el que se hizo clicfuente
Usé este módulo (no tengo ninguna asociación con el autor)
Hizo el trabajo bien.
fuente
"Support for the experimental syntax 'classProperties' isn't currently enabled"
esto simplemente funcionó de inmediato. Para cualquiera que pruebe esta solución, recuerde borrar cualquier código persistente que haya copiado al probar las otras soluciones. por ejemplo, agregar oyentes de eventos parece estar en conflicto con esto.Hice esto en parte siguiendo esto y siguiendo los documentos oficiales de React sobre el manejo de referencias que requieren reaccionar ^ 16.3. Esto es lo único que me funcionó después de probar algunas de las otras sugerencias aquí ...
fuente
Un ejemplo con estrategia
Me gustan las soluciones proporcionadas que hacen lo mismo creando un contenedor alrededor del componente.
Como se trata más de un comportamiento, pensé en Estrategia y se me ocurrió lo siguiente.
Soy nuevo con React y necesito un poco de ayuda para ahorrar algo de repetitivo en los casos de uso.
Por favor revisa y dime lo que piensas.
ClickOutsideBehavior
Uso de muestra
Notas
Pensé en almacenar los
component
ganchos del ciclo de vida pasados y anularlos con métodos similares a esto:component
es el componente pasado al constructor deClickOutsideBehavior
.Esto eliminará la placa de activación / desactivación del usuario de este comportamiento, pero no se ve muy bien.
fuente
En mi caso DROPDOWN, la solución de Ben Bud funcionó bien, pero tenía un botón de alternar separado con un controlador onClick. Por lo tanto, la lógica de clic externa entró en conflicto con el botón onClick toggler. Así es como lo resolví pasando también la referencia del botón:
fuente
Si desea utilizar un componente pequeño (466 Bytes comprimido) que ya existe para esta funcionalidad, puede consultar esta biblioteca react-outclick .
Lo bueno de la biblioteca es que también le permite detectar clics fuera de un componente y dentro de otro. También es compatible con la detección de otros tipos de eventos.
fuente
Si desea utilizar un componente pequeño (466 Bytes comprimido) que ya existe para esta funcionalidad, puede consultar esta biblioteca react-outclick . Captura eventos fuera de un componente de reacción o elemento jsx.
Lo bueno de la biblioteca es que también le permite detectar clics fuera de un componente y dentro de otro. También es compatible con la detección de otros tipos de eventos.
fuente
Sé que esta es una vieja pregunta, pero sigo encontrando esto y tuve muchos problemas para resolver esto en un formato simple. Entonces, si esto facilitaría la vida de cualquiera, use OutsideClickHandler de airbnb. Es el complemento más simple para realizar esta tarea sin escribir su propio código.
Ejemplo:
fuente
fuente
Puedes encontrar una manera simple de resolver tu problema, te muestro:
....
esto funciona para mí, buena suerte;)
fuente
Encontré esto en el artículo a continuación:
render () {return ({this.node = node;}}> Toggle Popover {this.state.popupVisible && (I'm a popover!)}); }}
Aquí hay un gran artículo sobre este tema: "Manejar clics fuera de React components" https://larsgraubner.com/handle-outside-clicks-react/
fuente
Agregue un
onClick
controlador a su contenedor de nivel superior e incremente un valor de estado cada vez que el usuario haga clic dentro. Pase ese valor al componente relevante y siempre que el valor cambie, puede trabajar.En este caso, estamos llamando
this.closeDropdown()
cada vezclickCount
que cambia el valor.El
incrementClickCount
método se activa dentro del.app
contenedor, pero no.dropdown
porque usamosevent.stopPropagation()
para evitar el burbujeo de eventos.Su código puede terminar pareciéndose a esto:
fuente
Para que la solución 'focus' funcione para el menú desplegable con oyentes de eventos, puede agregarlos con el evento onMouseDown en lugar de onClick . De esa manera, el evento se disparará y después de eso, la ventana emergente se cerrará así:
fuente
fuente
import ReactDOM from 'react-dom';
Hice una solución para todas las ocasiones.
Debe usar un Componente de orden superior para ajustar el componente que desea escuchar para los clics que se encuentran fuera de él.
Este ejemplo de componente tiene solo un accesorio: "onClickedOutside" que recibe una función.
fuente
UseOnClickOutside Hook - Reacciona 16.8 +
Crear una función general useOnOutsideClick
Luego use el gancho en cualquier componente funcional.
Enlace a demo
Parcialmente inspirado por la respuesta de @ pau1fitzgerald.
fuente