Comment empêcher l'événement onclick d'un parent de se déclencher lorsque l'on clique sur une ancre enfant?

347

J'utilise actuellement jQuery pour rendre un div cliquable et dans ce div, j'ai également des ancres. Le problème que je rencontre est que lorsque je clique sur une ancre, les deux événements de clic se déclenchent (pour la div et l'ancre). Comment puis-je empêcher l'événement onclick de la division de se déclencher lorsqu'un clic sur une ancre est activé?

Voici le code cassé:

Javascript

var url = $("#clickable a").attr("href");

$("#clickable").click(function() {
    window.location = url;
    return true;
})

HTML

<div id="clickable">
    <!-- Other content. -->
    <a href="http://foo.com">I don't want #clickable to handle this click event.</a>
</div>
Jonathon Watney
la source

Réponses:

459

Les événements remontent au point le plus élevé du DOM auquel un événement de clic a été attaché. Donc, dans votre exemple, même si vous n'aviez aucun autre élément explicitement cliquable dans le div, chaque élément enfant du div ferait bouillonner son événement click dans le DOM jusqu'à ce que le gestionnaire d'événement click du DIV l'attrape.

Il y a deux solutions à cela: vérifier pour savoir qui est à l'origine de l'événement. jQuery transmet un objet eventargs avec l'événement:

$("#clickable").click(function(e) {
    var senderElement = e.target;
    // Check if sender is the <div> element e.g.
    // if($(e.target).is("div")) {
    window.location = url;
    return true;
});

Vous pouvez également attacher un gestionnaire d'événements Click à vos liens qui leur dit d' arrêter la propagation d'événements après l'exécution de leur propre gestionnaire:

$("#clickable a").click(function(e) {
   // Do something
   e.stopPropagation();
});
Rex M
la source
3
Très bonne réponse. Heureux de savoir qu'il existe également des options.
Jonathon Watney le
4
+1! Attacher un gestionnaire de clics avec stopPropagation est une très bonne astuce, merci!
Thomas
2
si vous aviez un tas d'éléments sur lesquels vous vouliez empêcher la propagation, vous pourriez trouver leur élément parent et la tête l'empêcher de bouillonner là-haut aussi. Donc, plutôt que d'intercepter tous les liens a dans un div, disons par exemple - interceptez simplement le clic même sur le div lui-même et empêchez-le d'aller plus haut et vous êtes prêt.
sucitivel
21
L'utilisation if( e.target !== this) return;chez le parent est meilleure que e.stopPropagation()chez l'enfant car vous ne savez jamais si quelqu'un d'autre attache un gestionnaire aux enfants, ou si une bibliothèque doit attacher un gestionnaire à un enfant (et vous ne voulez pas jouer avec le code de la bibliothèque). C'est une meilleure séparation des préoccupations.
UTF_or_Death
3
Placer la if( e.target !== this) return;coche dans le parent signifie que si l'un des autres enfants de ce parent qui n'ont pas de gestionnaire onClick est cliqué, rien ne se passe. Il est donc inutile pour une situation où vous avez par exemple un parent div cliquable. e.stopPropagation()fonctionne bien cependant.
derf26
100

Utilisez la méthode stopPropagation , voir un exemple:

$("#clickable a").click(function(e) {
   e.stopPropagation();
});

Comme l'a dit jQuery Docs:

stopPropagation empêche l'événement de se propager dans l'arborescence DOM, empêchant les gestionnaires parents d'être notifiés de l'événement.

Gardez à l'esprit que cela n'empêche pas les autres écouteurs de gérer cet événement (par exemple, plus d'un gestionnaire de clic pour un bouton), si ce n'est pas l'effet souhaité, vous devez utiliser à la stopImmediatePropagationplace.

Cleiton
la source
48

Voici ma solution pour tous ceux qui recherchent un code non jQuery (javascript pur)

document.getElementById("clickable").addEventListener("click", function( e ){
    e = window.event || e; 
    if(this === e.target) {
        // put your code here
    }
});

Votre code ne sera pas exécuté si vous cliquez sur les enfants des parents

Sabaz
la source
1
Cela a fonctionné pour moi, j'avais besoin d'une solution non JS / jQuery. 10x!
LA
Très bonne réponse. Je viens de passer l'événement directement. Je n'ai donc pas utilisé window.eventce que MDN décourage: developer.mozilla.org/en-US/docs/Web/API/Window/event . Si vous pouvez gagner du temps, je vous serais reconnaissant de bien vouloir expliquer pourquoi vous l'avez utilisé window.event(c'est peut-être un problème qui existait en 2015 ou je n'ai pas compris la raison).
RMo
15

vous pouvez également essayer ceci

$("#clickable").click(function(event) {
   var senderElementName = event.target.tagName.toLowerCase();
   if(senderElementName === 'div')
   {
       // do something here 
   } 
   else
   {
      //do something with <a> tag
   }
});
Rashad Annara
la source
14

Si vous n'avez pas l'intention d'interagir avec les éléments internes dans tous les cas, une solution CSS peut vous être utile.

Réglez simplement le ou les éléments intérieurs sur pointer-events: none

dans ton cas:

.clickable > a {
    pointer-events: none;
}

ou pour cibler tous les éléments internes en général:

.clickable * {
    pointer-events: none;
}

Ce hack facile m'a fait gagner beaucoup de temps lors du développement avec ReactJS

Le support du navigateur peut être trouvé ici: http://caniuse.com/#feat=pointer-events

Hooman Askari
la source
8

Si vous avez plusieurs éléments dans le div cliquable, vous devez le faire:

$('#clickable *').click(function(e){ e.stopPropagation(); });
Ivan Ivković
la source
8

L'utilisation de return false;ou e.stopPropogation();ne permettra pas l'exécution de code supplémentaire. Il arrêtera le flux à ce point lui-même.

Imamudin Naseem
la source
Oui, c'est important - je l'ai rencontré moi-même. Mais la réponse ci-dessus de Rex est utile - peut obtenir l'élément sur lequel vous avez cliqué et, dans certains cas, l'utiliser dans la logique que vous essayez d'arrêter. .target.nodeName a également été utile pour avoir une idée claire de ce qui a été touché.
Watercayman
8

Écrire si quelqu'un a besoin (a travaillé pour moi):

event.stopImmediatePropagation()

De cette solution.

Bahadir Tasdemir
la source
4

Alternative en ligne:

<div>
    <!-- Other content. -->
    <a onclick='event.stopPropagation();' href="http://foo.com">I don't want #clickable to handle this click event.</a>
</div>
Matt Wyeth
la source
3

Voici un exemple d'utilisation d'Angular 2+

Par exemple, si vous souhaitez fermer un composant modal si l'utilisateur clique en dehors de celui-ci:

// Close the modal if the document is clicked.

@HostListener('document:click', ['$event'])
public onDocumentClick(event: MouseEvent): void {
  this.closeModal();
}

// Don't close the modal if the modal itself is clicked.

@HostListener('click', ['$event'])
public onClick(event: MouseEvent): void {
  event.stopPropagation();
}
Steve Brush
la source
1

Vous devez empêcher l'événement d'atteindre (se propager) au parent (le div). Voir la partie sur le bullage ici et les informations API spécifiques à jQuery ici .

Matt Ball
la source
Cool. Merci pour les informations sur le bouillonnement de l'événement.
Jonathon Watney, le
1

var inner = document.querySelector("#inner");
var outer = document.querySelector("#outer");
inner.addEventListener('click',innerFunction);
outer.addEventListener('click',outerFunction);

function innerFunction(event){
  event.stopPropagation();
  console.log("Inner Functiuon");
}

function outerFunction(event){
  console.log("Outer Functiuon");
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Pramod Kharade-Event with Outer and Inner Progration</title>
</head>
<body>
<div id="outer" style="width:100px;height:100px;background-color:green;">
  <div id="inner" style="width:35px;height:35px;background-color:yellow;"></div>
</div>
</body>
</html>

Pramod Kharade
la source
0

Pour spécifier un sous-élément comme non cliquable, écrivez la hiérarchie css comme dans l'exemple ci-dessous.

Dans cet exemple, j'arrête la propagation vers tous les éléments (*) à l'intérieur de td à l'intérieur de tr à l'intérieur d'une table avec la classe ".subtable"

$(document).ready(function()
{    
   $(".subtable tr td *").click(function (event)
   {
       event.stopPropagation();
   });

});
user3202819
la source
0

ignoreParent () est une solution JavaScript pure.

Il fonctionne comme une couche intermédiaire qui compare les coordonnées du clic de la souris avec les coordonnées des éléments enfants. Deux étapes d'implémentation simples:

1. Mettez le code ignoreParent () sur votre page.

2. Au lieu de l'original onclick = "parentEvent ();" du parent , écrire:

onclick="ignoreParent(['parentEvent()', 'child-ID']);"

Vous pouvez transmettre des ID de n'importe quel nombre d'éléments enfants à la fonction et en exclure d'autres.

Si vous avez cliqué sur l'un des éléments enfants, l'événement parent ne se déclenche pas. Si vous avez cliqué sur parent, mais sur aucun des éléments enfants [fournis comme arguments], l'événement parent est déclenché.

Code ignoreParent () sur Github

Fom
la source
0

S'il est dans un contexte en ligne, en HTML, essayez ceci:

onclick="functionCall();event.stopPropagation();
Gleno
la source
-1

Au cas où quelqu'un aurait eu ce problème en utilisant React, voici comment je l'ai résolu.

scss:

#loginBackdrop {
position: absolute;
width: 100% !important;
height: 100% !important;
top:0px;
left:0px;
z-index: 9; }

#loginFrame {
width: $iFrameWidth;
height: $iFrameHeight;
background-color: $mainColor;
position: fixed;
z-index: 10;
top: 50%;
left: 50%;
margin-top: calc(-1 * #{$iFrameHeight} / 2);
margin-left: calc(-1 * #{$iFrameWidth} / 2);
border: solid 1px grey;
border-radius: 20px;
box-shadow: 0px 0px 90px #545454; }

Rendu du composant ():

render() {
    ...
    return (
        <div id='loginBackdrop' onClick={this.props.closeLogin}>
            <div id='loginFrame' onClick={(e)=>{e.preventDefault();e.stopPropagation()}}>
             ... [modal content] ...
            </div>
        </div>
    )
}

En ajoutant une fonction onClick pour le modal enfant (content div), les événements de clic de souris sont empêchés d'atteindre la fonction 'closeLogin' de l'élément parent.

Cela a fait l'affaire pour moi et j'ai pu créer un effet modal avec 2 divs simples.

Claudio
la source
-3

ajouter acomme suit:

<a href="http://foo.com" onclick="return false;">....</a>

ou à return false;partir du gestionnaire de clic pour #clickablecomme:

  $("#clickable").click(function() {
        var url = $("#clickable a").attr("href");
        window.location = url;
        return false;
   });
TheVillageIdiot
la source
-3

Toutes les solutions sont compliquées et de jscript. Voici la version la plus simple:

var IsChildWindow=false;

function ParentClick()
{
    if(IsChildWindow==true)
    {
        IsChildWindow==false;
        return;
    }
    //do ur work here   
}


function ChildClick()
{
    IsChildWindow=true;
    //Do ur work here    
}
ratnesh
la source
3
je pense que vous avez fait une erreur dans la ligne "IsChildWindow == false;" - ne devrait-il pas s'agir de "IsChildWindow = false;"?
Andre Schweighofer
-5
<a onclick="return false;" href="http://foo.com">I want to ignore my parent's onclick event.</a>
andres descalzo
la source
5
Cela empêche également le lien de naviguer.
Rex M
Oui, ou pas ce qui est nécessaire?. Ou besoin d'être nevegar?
andres descalzo