Je recherche un moyen de détecter si un événement de clic s'est produit en dehors d'un composant, comme décrit dans cet article . jQuery le plus proche () est utilisé pour voir si la cible d'un événement click a l'élément dom comme l'un de ses parents. S'il y a correspondance, l'événement click appartient à l'un des enfants et n'est donc pas considéré comme étant en dehors du composant.
Donc, dans mon composant, je veux attacher un gestionnaire de clics à la fenêtre. Lorsque le gestionnaire se déclenche, je dois comparer la cible avec les enfants dom de mon composant.
L'événement click contient des propriétés telles que "chemin" qui semble contenir le chemin dom que l'événement a parcouru. Je ne sais pas quoi comparer ou comment le traverser au mieux, et je pense que quelqu'un doit déjà avoir mis cela dans une fonction utilitaire intelligente ... Non?
la source
Réponses:
L'utilisation des références dans React 16.3+ a changé.
La solution suivante utilise ES6 et suit les meilleures pratiques pour la liaison ainsi que la définition de la référence via une méthode.
Pour le voir en action:
Implémentation de classe:
Mise en œuvre des crochets:
la source
document.addEventListener
ci?context
n'est nécessaire comme argument dans le constructeur que s'il est utilisé. Il n'est pas utilisé dans ce cas. La raison pour laquelle l'équipe de réaction recommande d'avoirprops
comme argument dans le constructeur est que l'utilisation dethis.props
avant l'appelsuper(props)
ne sera pas définie et peut conduire à des erreurs.context
est toujours disponible sur les nœuds internes, c'est tout le but decontext
. De cette façon, vous n'avez pas à le transmettre d'un composant à l'autre comme vous le faites avec les accessoires. 2. Il s'agit simplement d'une préférence stylistique et ne justifie pas un débat dans ce cas.Voici la solution qui a fonctionné le mieux pour moi sans attacher d'événements au conteneur:
Certains éléments HTML peuvent avoir ce qui est appelé " focus ", par exemple des éléments d'entrée. Ces éléments répondront également au flou événement , lorsqu'ils perdront cette concentration.
Pour donner à n'importe quel élément la capacité d'avoir le focus, assurez-vous simplement que son attribut tabindex est défini sur autre chose que -1. En HTML standard, ce serait en définissant l'
tabindex
attribut, mais dans React, vous devez utilisertabIndex
(notez la majusculeI
).Vous pouvez également le faire via JavaScript avec
element.setAttribute('tabindex',0)
C'est pour cela que je l'utilisais, pour créer un menu DropDown personnalisé.
la source
display:absolute
sorte que le menu déroulant n'affecte pas la taille de la div parent. Cela signifie que lorsque je clique sur un élément dans la liste déroulante, le flou se déclenche.onBlur
seront déclenchés sur votre conteneur déroulant etexpanded
seront définis sur faux.J'étais coincé sur le même problème. Je suis un peu en retard à la fête ici, mais pour moi, c'est une très bonne solution. J'espère que cela sera utile à quelqu'un d'autre. Vous devez importer
findDOMNode
dereact-dom
Approche React Hooks (16.8 +)
Vous pouvez créer un crochet réutilisable appelé
useComponentVisible
.Ensuite, dans le composant, vous souhaitez ajouter la fonctionnalité pour effectuer les opérations suivantes:
Trouvez un exemple de code et de boîte ici.
la source
ReactDOM.findDOMNode
et est obsolète, devrait utiliser desref
rappels: github.com/yannickcr/eslint-plugin-react/issues/…Après avoir essayé de nombreuses méthodes ici, j'ai décidé d'utiliser github.com/Pomax/react-onclickoutside en raison de sa complétude.
J'ai installé le module via npm et l'ai importé dans mon composant:
Ensuite, dans ma classe de composants, j'ai défini la
handleClickOutside
méthode:Et lors de l'exportation de mon composant, je l'ai enveloppé dans
onClickOutside()
:C'est ça.
la source
tabIndex
/onBlur
approche proposée par Pablo ? Comment fonctionne la mise en œuvre et en quoi son comportement diffère-t-il de l'onBlur
approche?tabIndex/onBlur
approche. Cela ne fonctionne pas lorsque la liste déroulante estposition:absolute
, comme un menu survolant un autre contenu.J'ai trouvé une solution grâce à Ben Alpert sur discuter.reactjs.org . L'approche suggérée associe un gestionnaire au document, mais cela s'est avéré problématique. En cliquant sur l'un des composants de mon arborescence, un nouveau rendu a supprimé l'élément cliqué lors de la mise à jour. Étant donné que le rendu de React se produit avant l'appel du gestionnaire de corps de document, l'élément n'a pas été détecté comme «à l'intérieur» de l'arborescence.
La solution était d'ajouter le gestionnaire sur l'élément racine de l'application.
principale:
composant:
la source
document
est pas?mousedown
serait probablement un meilleur gestionnaireclick
qu'ici. Dans la plupart des applications, le comportement de fermeture du menu en cliquant sur l'extérieur se produit au moment où vous cliquez avec le bouton droit de la souris, pas lorsque vous relâchez. Essayez-le, par exemple, avec l' indicateur Stack Overflow ou les dialogues de partage ou avec l'une des listes déroulantes de la barre de menu supérieure de votre navigateur.ReactDOM.findDOMNode
et la chaîneref
sont obsolètes, doivent utiliser desref
rappels: github.com/yannickcr/eslint-plugin-react/issues/…document
Mise en œuvre du crochet basée sur l'excellent discours de Tanner Linsley au JSConf Hawaii 2020 :
useOuterClick
API de crochetla mise en oeuvre
useOuterClick
utilise des références mutables pour créer une API allégée pour leClient
. Un rappel peut être défini sans avoir à le mémoriser viauseCallback
. Le corps de rappel a toujours accès aux accessoires et à l'état les plus récents (pas de valeurs de fermeture périmées).Remarque pour les utilisateurs iOS
iOS ne traite que certains éléments comme cliquables. Pour contourner ce comportement, choisissez un écouteur de clic externe différent de
document
- rien vers le haut, y comprisbody
. Par exemple, vous pouvez ajouter un écouteur sur la racine Reactdiv
dans l'exemple ci-dessus et augmenter sa hauteur (height: 100vh
ou similaire) pour intercepter tous les clics extérieurs. Sources: quirksmode.org et cette réponsela source
@media (hover: none) and (pointer: coarse) { body { cursor:pointer } }
L'ajout de cela dans mes styles globaux semble avoir résolu le problème.@supports (-webkit-overflow-scrolling: touch)
qui cible uniquement IOS même si je n'en sais pas plus! Je viens de le tester et ça marche.Aucune des autres réponses ici n'a fonctionné pour moi. J'essayais de cacher une fenêtre contextuelle sur le flou, mais comme le contenu était absolument positionné, onBlur tirait même au clic du contenu intérieur.
Voici une approche qui a fonctionné pour moi:
J'espère que cela t'aides.
la source
document
. Merci.[Mise à jour] Solution avec React ^ 16.8 utilisant des crochets
CodeSandbox
Solution avec React ^ 16.3 :
CodeSandbox
la source
.contains is not a function
, cela peut être dû au fait que vous transmettez l'hélice ref à un composant personnalisé plutôt qu'à un véritable élément DOM comme un<div>
ref
hélice à un composant personnalisé, vous voudrez peut-être jeter un œil àReact.forwardRef
Voici mon approche (démo - https://jsfiddle.net/agymay93/4/ ):
J'ai créé un composant spécial appelé
WatchClickOutside
et il peut être utilisé comme (je suppose que laJSX
syntaxe):Voici le code du
WatchClickOutside
composant:la source
ref
est obsolète, doit utiliser desref
rappels: reactjs.org/docs/refs-and-the-dom.htmlCela a déjà de nombreuses réponses, mais elles ne répondent pas
e.stopPropagation()
et empêchent de cliquer sur les liens de réaction en dehors de l'élément que vous souhaitez fermer.Étant donné que React possède son propre gestionnaire d'événements artificiels, vous ne pouvez pas utiliser le document comme base pour les écouteurs d'événements. Vous devez le faire
e.stopPropagation()
avant car React utilise le document lui-même. Si vous utilisez par exemple à ladocument.querySelector('body')
place. Vous pouvez empêcher le clic du lien React. Voici un exemple de la façon dont j'implémente le clic à l'extérieur et la fermeture.Il utilise ES6 et React 16.3 .
la source
Pour ceux qui ont besoin d'un positionnement absolu, une option simple que j'ai choisie consiste à ajouter un composant wrapper conçu pour couvrir toute la page avec un arrière-plan transparent. Ensuite, vous pouvez ajouter un onClick sur cet élément pour fermer votre composant interne.
Comme c'est le cas actuellement si vous ajoutez un gestionnaire de clics sur le contenu, l'événement sera également propagé à la division supérieure et déclenchera donc le gestionnaireOutsideClick. Si ce n'est pas le comportement souhaité, arrêtez simplement la progression de l'événement sur votre gestionnaire.
"
la source
Pour prolonger la réponse acceptée de Ben Bud , si vous utilisez des composants de style, transmettre des références de cette façon vous donnera une erreur telle que "this.wrapperRef.contains n'est pas une fonction".
La correction suggérée, dans les commentaires, pour envelopper le composant stylisé avec un div et y passer la référence, fonctionne. Cela dit, dans leurs documents, ils expliquent déjà la raison de cela et la bonne utilisation des références dans les composants de style:
Ainsi:
Ensuite, vous pouvez y accéder directement dans la fonction "handleClickOutside":
Cela vaut également pour l'approche "onBlur":
la source
Material-UI a un petit composant pour résoudre ce problème: https://material-ui.com/components/click-away-listener/ que vous pouvez choisir. Il pèse 1,5 ko gzippé, il prend en charge les mobiles, IE 11 et les portails.
la source
Ma plus grande préoccupation avec toutes les autres réponses est de devoir filtrer les événements de clic de la racine / du parent vers le bas. J'ai trouvé que le moyen le plus simple était de simplement définir un élément frère avec position: fixed, un z-index 1 derrière la liste déroulante et de gérer l'événement click sur l'élément fixe à l'intérieur du même composant. Garde tout centralisé dans un composant donné.
Exemple de code
la source
L'événement
path[0]
est le dernier élément cliquéla source
J'ai utilisé ce module (je n'ai aucun lien avec l'auteur)
Cela a bien fonctionné.
la source
"Support for the experimental syntax 'classProperties' isn't currently enabled"
cela, cela a fonctionné directement. Pour tous ceux qui essaient cette solution, n'oubliez pas d'effacer tout code persistant que vous pourriez avoir copié lors de l'essai des autres solutions. Par exemple, l'ajout d'écouteurs d'événements semble entrer en conflit avec cela.Je l'ai fait en partie en suivant cela et en suivant les documents officiels de React sur la gestion des références, ce qui nécessite de réagir ^ 16.3. C'est la seule chose qui a fonctionné pour moi après avoir essayé certaines des autres suggestions ici ...
la source
Un exemple avec la stratégie
J'aime les solutions fournies qui permettent de faire la même chose en créant un wrapper autour du composant.
Puisqu'il s'agit plus d'un comportement, j'ai pensé à la stratégie et j'ai proposé ce qui suit.
Je suis nouveau avec React et j'ai besoin d'un peu d'aide pour économiser du passe-partout dans les cas d'utilisation
Vérifiez et dites moi ce que vous en pensez s'il vous plait.
ClickOutsideBehavior
Exemple d'utilisation
Remarques
J'ai pensé à stocker les
component
crochets de cycle de vie passés et à les remplacer par des méthodes similaires à ceci:component
est le composant passé au constructeur deClickOutsideBehavior
.Cela supprimera l'activation / désactivation du passe-partout de l'utilisateur de ce comportement, mais cela n'a pas l'air très agréable
la source
Dans mon cas DROPDOWN, la solution de Ben Bud fonctionnait bien, mais j'avais un bouton bascule séparé avec un gestionnaire onClick. Ainsi, la logique de clic extérieur était en conflit avec le bouton du bouton bascule. Voici comment je l'ai résolu en passant également la référence du bouton:
la source
Si vous souhaitez utiliser un petit composant (466 octets compressé) qui existe déjà pour cette fonctionnalité, vous pouvez consulter cette bibliothèque react-outclick .
La bonne chose à propos de la bibliothèque est qu'elle vous permet également de détecter les clics à l'extérieur d'un composant et à l'intérieur d'un autre. Il prend également en charge la détection d'autres types d'événements.
la source
Si vous souhaitez utiliser un petit composant (466 octets compressé) qui existe déjà pour cette fonctionnalité, vous pouvez consulter cette bibliothèque react-outclick . Il capture les événements en dehors d'un composant React ou d'un élément jsx.
La bonne chose à propos de la bibliothèque est qu'elle vous permet également de détecter les clics à l'extérieur d'un composant et à l'intérieur d'un autre. Il prend également en charge la détection d'autres types d'événements.
la source
Je sais que c'est une vieille question, mais je continue de la rencontrer et j'ai eu beaucoup de mal à la comprendre dans un format simple. Donc, si cela faciliterait la vie de quiconque, utilisez OutsideClickHandler par airbnb. C'est le plugin le plus simple pour accomplir cette tâche sans écrire votre propre code.
Exemple:
la source
la source
vous pouvez vous un moyen simple de résoudre votre problème, je vous montre:
....
cela fonctionne pour moi, bonne chance;)
la source
J'ai trouvé cela dans l'article ci-dessous:
render () {return ({this.node = node;}}> Toggle Popover {this.state.popupVisible && (Je suis un popover!)}); }}
Voici un excellent article sur ce problème: "Gérer les clics en dehors des composants React" https://larsgraubner.com/handle-outside-clicks-react/
la source
Ajoutez un
onClick
gestionnaire à votre conteneur de niveau supérieur et incrémentez une valeur d'état chaque fois que l'utilisateur clique dessus. Transmettez cette valeur au composant approprié et chaque fois que la valeur change, vous pouvez travailler.Dans ce cas, nous appelons
this.closeDropdown()
chaque fois que laclickCount
valeur change.La
incrementClickCount
méthode se déclenche dans le.app
conteneur, mais pas.dropdown
parce que nous l'utilisonsevent.stopPropagation()
pour empêcher la propagation d'événements.Votre code peut finir par ressembler à ceci:
la source
Pour que la solution «focus» fonctionne pour la liste déroulante avec les écouteurs d'événements, vous pouvez les ajouter avec l' événement onMouseDown au lieu de onClick . De cette façon, l'événement se déclenchera et après cela, le popup se fermera comme suit:
la source
la source
import ReactDOM from 'react-dom';
J'ai fait une solution pour toutes les occasions.
Vous devez utiliser un composant d'ordre élevé pour envelopper le composant que vous souhaitez écouter pour les clics en dehors de celui-ci.
Cet exemple de composant n'a qu'un seul accessoire: "onClickedOutside" qui reçoit une fonction.
la source
Crochet UseOnClickOutside - React 16.8 +
Créer une fonction useOnOutsideClick générale
Utilisez ensuite le crochet dans n'importe quel composant fonctionnel.
Lien vers la démo
Partiellement inspiré par la réponse @ pau1fitzgerald.
la source