Clic en dehors d'un élément : 5 erreurs fatales et comment les corriger

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi "click outside" est un casse-tête pour chaque développeur front-end ?

\n\n

Imaginez : vous créez une fenêtre modale, un menu déroulant ou une liste déroulante. L'utilisateur l'ouvre, puis clique quelque part dans le vide. Logique ? Oui. Simple à implémenter ? C'est là que les ennuis commencent.

\n\n

La question "How do I detect a click outside an element?" est l'une des plus populaires sur Stack Overflow. Des milliers de développeurs se cassent les dents en essayant de comprendre pourquoi leur code fonctionne tantôt, tantôt pas. Aujourd'hui, nous allons analyser les erreurs les plus courantes lors de la détection d'un clic en dehors d'un élément en JavaScript et apprendre à le faire correctement.

\n\n

Cet article est rédigé pour le portail éducatif pythonlib.ru, mais le code sera en JavaScript pur — aucun framework, que du hardcore. C'est parti !

\n\n

Erreur n°1 : Écouter le clic sur l'élément lui-même

\n\n

L'erreur la plus naïve, mais courante. Les débutants tentent d'attacher un gestionnaire à l'élément et d'attendre que l'utilisateur clique à côté. Cela ressemble à : "Je ne sais pas quand l'utilisateur cliquera en dehors, mais je vais marquer cet événement".

\n\n

Cela ne fonctionne pas. L'événement click ne se déclenche que lorsque l'utilisateur clique sur l'élément. Si le clic est en dehors, l'événement n'est pas généré. Vous ne saurez tout simplement pas qu'un clic a eu lieu.

\n\n

Comment faire correctement ?

\n

Il faut écouter le clic sur le document (ou sur window), puis vérifier si le clic a eu lieu à l'intérieur ou à l'extérieur de votre élément.

\n\n
const myElement = document.querySelector('.my-dropdown');\n\ndocument.addEventListener('click', function(event) {\n  // Vérifie si on a cliqué à l'intérieur de myElement\n  if (!myElement.contains(event.target)) {\n    // Clic à l'extérieur — on ferme !\n    myElement.style.display = 'none';\n  }\n});
\n\n

C'est la base. Mais il y a aussi des pièges. Passons à l'erreur suivante.

\n\n

Erreur n°2 : Ignorer la propagation des événements (Event Bubbling)

\n\n

Vous avez écrit le code ci-dessus, mais il ne fonctionne toujours pas ? Le problème vient probablement de la propagation des événements. Lorsque vous cliquez sur un élément enfant, l'événement se déclenche d'abord sur celui-ci, puis "remonte" dans l'arbre DOM jusqu'au document.

\n\n

Si quelque part sur le chemin se trouve un event.stopPropagation(), l'événement mourra et n'atteindra pas le document. Votre gestionnaire ne se déclenchera pas.

\n\n

Comment l'éviter ?

\n

N'utilisez jamais stopPropagation() sans nécessité absolue. Si vous écrivez un composant qui intercepte le clic (par exemple, un bouton dans une liste déroulante), utilisez event.stopImmediatePropagation() uniquement si vous êtes sûr que cela ne cassera pas les gestionnaires externes.

\n\n

La meilleure solution est de ne pas compter sur la propagation, mais d'attacher le gestionnaire en phase de capture (capture phase). Pour cela, passez le troisième argument true à addEventListener :

\n\n
document.addEventListener('click', function(event) {\n  // Votre code\n}, true); // Phase capture : l'événement atteint d'abord document, puis descend
\n\n

Maintenant, l'événement sera intercepté avant que quelqu'un ne l'arrête. Mais c'est une arme à double tranchant — utilisez-la avec précaution.

\n\n

Erreur n°3 : Comparaison incorrecte avec event.target

\n\n

Un classique : vous vérifiez event.target === myElement. Cela fonctionne uniquement si l'utilisateur clique exactement sur l'élément lui-même. Mais que faire si à l'intérieur de l'élément il y a une image, un bouton ou simplement du texte ?

\n\n

Un clic sur un nœud enfant donnera event.target égal à ce nœud enfant, et non à votre élément. La condition ne sera pas remplie et l'élément ne se fermera pas.

\n\n

Solution : utilisez Node.contains()

\n

La méthode contains() vérifie si le nœud passé est un descendant du nœud donné (ou le nœud lui-même). C'est parfait pour notre tâche.

\n\n
// Correct\nif (!myElement.contains(event.target)) {\n  // Clic à l'extérieur\n}\n\n// Incorrect\nif (event.target !== myElement) {\n  // Ignorera les clics sur les éléments enfants\n}

Blogs

Book Recommendations