119 votes

Détecter un clic en dehors d'un élément (vanilla JavaScript)

J'ai cherché partout une bonne solution, mais je n'en ai pas trouvé une qui fasse ne pas utiliser jQuery .

Existe-t-il un moyen normal (sans bidouillage bizarre ou code facile à casser) de détecter un clic à l'extérieur d'un élément (qui peut ou non avoir des enfants) ?

282voto

bfred.it Points 2384

Ajouter un écouteur d'événements à document et utiliser Node.contains() pour savoir si la cible de l'événement (qui est l'élément cliqué le plus à l'intérieur) se trouve dans l'élément spécifié. Cela fonctionne même dans IE5

var specifiedElement = document.getElementById('a');

//I'm using "click" but it works with any event
document.addEventListener('click', function(event) {
  var isClickInside = specifiedElement.contains(event.target);

  if (!isClickInside) {
    //the click was outside the specifiedElement, do something
  }
});

var specifiedElement = document.getElementById('a');

//I'm using "click" but it works with any event
document.addEventListener('click', function(event) {
  var isClickInside = specifiedElement.contains(event.target);
  if (isClickInside) {
    alert('You clicked inside A')
  } else {
    alert('You clicked outside A')
  }
});

div {
  margin: auto;
  padding: 1em;
  max-width: 6em;
  background: rgba(0, 0, 0, .2);
  text-align: center;
}

Is the click inside A or outside?
<div id="a">A
  <div id="b">B
    <div id="c">C</div>
  </div>
</div>

25voto

metadings Points 1705

Vous devez gérer l'événement de clic au niveau du document. Dans l'objet événement, vous avez un target l'élément DOM le plus à l'intérieur qui a été cliqué. Avec cela, vous vous vérifiez et remontez ses parents jusqu'à l'élément document, si l'un d'entre eux est l'élément que vous surveillez.

Voir l'exemple sur jsFiddle

document.addEventListener("click", function (e) {
  var level = 0;
  for (var element = e.target; element; element = element.parentNode) {
    if (element.id === 'x') {
      document.getElementById("out").innerHTML = (level ? "inner " : "") + "x clicked";
      return;
    }
    level++;
  }
  document.getElementById("out").innerHTML = "not x clicked";
});

Comme toujours, cette méthode n'est pas compatible avec tous les navigateurs à cause de addEventListener/attachEvent, mais elle fonctionne comme suit.

Un enfant est cliqué, lorsque non pas event.target, mais un de ses parents est l'élément surveillé (je compte simplement les niveaux pour cela). Vous pouvez également avoir une variable booléenne, si l'élément est trouvé ou non, pour ne pas retourner le gestionnaire à l'intérieur de la clause for. Mon exemple se limite à ce que le gestionnaire ne se termine que lorsque rien ne correspond.

Pour assurer la compatibilité entre navigateurs, je procède généralement de la manière suivante :

var addEvent = function (element, eventName, fn, useCapture) {
  if (element.addEventListener) {
    element.addEventListener(eventName, fn, useCapture);
  }
  else if (element.attachEvent) {
    element.attachEvent(eventName, function (e) {
      fn.apply(element, arguments);
    }, useCapture);
  }
};

Il s'agit d'un code compatible avec les navigateurs pour attacher un écouteur/un gestionnaire d'événement, y compris la réécriture de this dans IE, pour être l'élément, comme le fait jQuery pour ses gestionnaires d'événements. Il y a beaucoup d'arguments pour avoir en tête certains éléments de jQuery ;)

8voto

Akhil Sekharan Points 6018

Que dites-vous de ça ?

Démonstration de jsBin

document.onclick = function(event){
  var hasParent = false;
    for(var node = event.target; node != document.body; node = node.parentNode)
    {
      if(node.id == 'div1'){
        hasParent = true;
        break;
      }
    }
  if(hasParent)
    alert('inside');
  else
    alert('outside');
}

0voto

Amrendra Kumar Points 73

J'ai fait beaucoup de recherches à ce sujet pour trouver une meilleure méthode. La méthode JavaScript .contains va récursivement dans le DOM pour vérifier s'il contient la cible ou non. Je l'ai utilisé dans un de mes projets react mais quand le DOM de react change sur l'état set, la méthode .contains ne fonctionne pas. J'ai donc trouvé la solution suivante

//Basic Html snippet
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
<div id="mydiv">
  <h2>
    click outside this div to test
  </h2>
  Check click outside 
</div>
</body>
</html>

//Implementation in Vanilla javaScript
const node = document.getElementById('mydiv')
//minor css to make div more obvious
node.style.width = '300px'
node.style.height = '100px'
node.style.background = 'red'

let isCursorInside = false

//Attach mouseover event listener and update in variable
node.addEventListener('mouseover', function() {
  isCursorInside = true
  console.log('cursor inside')
})

/Attach mouseout event listener and update in variable
node.addEventListener('mouseout', function() {
    isCursorInside = false
  console.log('cursor outside')
})

document.addEventListener('click', function() {
  //And if isCursorInside = false it means cursor is outside 
    if(!isCursorInside) {
    alert('Outside div click detected')
  }
})

DÉMO FONCTIONNELLE jsfiddle

-1voto

Gabriela C Points 1

Avec le code suivant vous pouvez détecter un clic en dehors d'un élément, je l'ai utilisé pour fermer une modale seulement quand on clique en dehors d'un élément.

 document.addEventListener("click", (e) => { 
    if(e.target === document.body){
       console.log("Click outside of an element");
 });

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X