Jak zapobiec uruchamianiu zdarzenia onclick rodzica po kliknięciu zakotwiczenia podrzędnego?

348

Obecnie używam jQuery, aby div mógł być klikalny, aw tym div też mam kotwice. Problem, na który wpadam, polega na tym, że po kliknięciu kotwicy uruchamiane są oba zdarzenia kliknięcia (dla div i kotwicy). Jak zapobiec uruchamianiu zdarzenia div po kliknięciu kotwicy?

Oto uszkodzony kod:

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
źródło

Odpowiedzi:

460

Bańki zdarzeń do najwyższego punktu w DOM, w którym zostało dołączone zdarzenie kliknięcia. Tak więc w twoim przykładzie, nawet jeśli nie masz żadnych innych elementów, które można wyraźnie kliknąć w div, każdy element potomny div będzie umieszczał bąbelki na zdarzeniu kliknięcia w górę do DOM, aż do momentu, gdy program obsługi zdarzeń kliknięcia DIV go złapie.

Istnieją dwa rozwiązania tego, aby sprawdzić, kto faktycznie zainicjował wydarzenie. jQuery przekazuje obiekt eventargs wraz ze zdarzeniem:

$("#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;
});

Możesz również dołączyć moduł obsługi zdarzeń kliknięcia do swoich linków, który każe im przestać propagować zdarzenia po wykonaniu przez własną procedurę obsługi:

$("#clickable a").click(function(e) {
   // Do something
   e.stopPropagation();
});
Rex M.
źródło
3
Świetna odpowiedź. Miło wiedzieć, że są też opcje.
Jonathon Watney
4
+1! Dołączanie modułu obsługi kliknięć za pomocą stopPropagation to bardzo fajna sztuczka, dzięki!
Thomas
2
jeśli miałeś masę elementów, na których chciałeś zapobiec rozprzestrzenianiu się, możesz znaleźć ich element nadrzędny i głowę, aby zapobiec jego propagacji. Więc zamiast przechwytywać wszystkie linki w div, powiedzmy na przykład - po prostu przechwyć kliknięcie nawet na samej div i nie pozwól, aby poszła wyżej i jesteś gotowy.
sucitivel
21
Używanie if( e.target !== this) return;w rodzicu jest lepsze niż e.stopPropagation()w dziecku, ponieważ nigdy nie wiadomo, czy ktoś inny dołącza do niego jakiś program obsługi lub czy biblioteka musi dołączyć program obsługi do dziecka (i nie chcesz zadzierać z kodem biblioteki). To lepsze rozdzielenie obaw.
UTF_or_Death
3
Wpisanie if( e.target !== this) return;kontroli w obiekcie nadrzędnym oznacza, że ​​jeśli zostanie kliknięte dowolne inne dziecko tego rodzica, które nie ma własnych procedur obsługi onClick, nic się nie dzieje. Jest to więc bezużyteczne w sytuacji, gdy masz np. Klikalny div rodzica. e.stopPropagation()działa jednak dobrze.
derf26
101

Użyj metody stopPropagation , zobacz przykład:

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

Jak powiedział jQuery Docs:

stopPropagation Metoda zapobiega propagacji zdarzenia do drzewa DOM, zapobiegając powiadamianiu o zdarzeniu przez wszystkie programy nadrzędne.

Pamiętaj, że nie przeszkadza to innym słuchaczom w obsłudze tego zdarzenia (np. Więcej niż jeden moduł obsługi kliknięcia dla przycisku), jeśli nie jest to pożądany efekt, musisz go użyć stopImmediatePropagation.

Cleiton
źródło
48

Oto moje rozwiązanie dla wszystkich, którzy szukają kodu innego niż jQuery (czysty javascript)

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

Twój kod nie zostanie wykonany, jeśli klikniesz na dziecko rodzica

Sabaz
źródło
1
To działało dla mnie, potrzebowałem rozwiązania innego niż JS / jQuery. 10x!
LA
Świetna odpowiedź. Właśnie zdałem zdarzenie bezpośrednio. Więc nie korzystałem window.event, co odradza MDN: developer.mozilla.org/en-US/docs/Web/API/Window/event . Jeśli możesz poświęcić czas, byłbym wdzięczny, jeśli możesz skomentować, dlaczego wykorzystałeś window.event(być może jest to problem, który istniał w 2015 roku lub nie zrozumiałem powodu).
RMo
15

możesz także spróbować tego

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

Jeśli nie zamierzasz wchodzić w interakcje z wewnętrznym elementem / elementami, rozwiązanie CSS może być dla Ciebie przydatne.

Wystarczy ustawić element wewnętrzny na pointer-events: none

w Twoim przypadku:

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

lub ogólnie kierować na wszystkie elementy wewnętrzne:

.clickable * {
    pointer-events: none;
}

Ten prosty hack zaoszczędził mi dużo czasu podczas programowania w ReactJS

Obsługę przeglądarki można znaleźć tutaj: http://caniuse.com/#feat=pointer-events

Hooman Askari
źródło
8

Jeśli masz wiele elementów w klikalnym div, powinieneś to zrobić:

$('#clickable *').click(function(e){ e.stopPropagation(); });
Ivan Ivković
źródło
8

Użycie return false;lub e.stopPropogation();nie pozwoli na wykonanie dalszego kodu. W tym momencie zatrzyma przepływ.

Imamudin Naseem
źródło
Tak, to ważne - sam na to wpadłem. Ale powyższa odpowiedź od Rexa jest pomocna - może uzyskać kliknięty element, a w niektórych przypadkach użyć go w logice, którą próbujesz zatrzymać. .target.nodeName był również pomocny, aby uzyskać jasny obraz tego, co zostało trafione.
Watercayman
8

Pisanie, jeśli ktoś potrzebuje (pracował dla mnie):

event.stopImmediatePropagation()

Z tego rozwiązania.

Bahadir Tasdemir
źródło
4

Inline Alternative:

<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
źródło
3

Oto przykład użycia Angulara 2+

Na przykład, jeśli chcesz zamknąć moduł modalny, jeśli użytkownik kliknie poza nim:

// 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
źródło
1

Musisz powstrzymać wydarzenie przed dotarciem do rodzica (div). Zobacz część o propagacji tutaj i informacje o interfejsie API specyficznym dla jQuery tutaj .

Matt Ball
źródło
Fajne. Dzięki za informację o propagowaniu wydarzeń.
Jonathon Watney
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
źródło
0

Aby określić jakiś element podrzędny jako niemożliwy do zapisania, napisz hierarchię css, jak w poniższym przykładzie.

W tym przykładzie zatrzymuję propagację do dowolnych elementów (*) wewnątrz td wewnątrz tr wewnątrz tabeli z klasą „.subtable”

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

});
użytkownik3202819
źródło
0

ignoreParent () jest czystym rozwiązaniem JavaScript.

Działa jako warstwa pośrednia, która porównuje współrzędne kliknięcia myszy ze współrzędnymi elementu / elementów potomnych. Dwa proste kroki wdrożenia:

1. Umieść kod ignoreParent () na swojej stronie.

2. Zamiast oryginalnego obiektu nadrzędnego onclick = "parentEvent ();" , pisać:

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

Możesz przekazać identyfikatory dowolnej liczby elementów potomnych do funkcji i wykluczyć inne.

Jeśli klikniesz jeden z elementów potomnych, zdarzenie nadrzędne nie zostanie uruchomione. Jeśli kliknąłeś element nadrzędny, ale nie element podrzędny [podany jako argument], zdarzenie nadrzędne zostanie uruchomione.

kod ignoreParent () na Github

Fom
źródło
0

Jeśli jest w kontekście wbudowanym, w HTML spróbuj tego:

onclick="functionCall();event.stopPropagation();
Gleno
źródło
-1

W przypadku, gdy ktoś miał ten problem przy użyciu React, tak to rozwiązałem.

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; }

Renderowanie komponentu ():

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

Dodając funkcję onClick do modalnego elementu potomnego (div treści), kliknięcia myszy zapobiegają osiągnięciu funkcji „closeLogin” elementu nadrzędnego.

To załatwiło sprawę i byłem w stanie stworzyć efekt modalny z 2 prostymi divami.

Claudio
źródło
-3

dodaj aw następujący sposób:

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

lub return false;z modułu obsługi kliknięć dla #clickable:

  $("#clickable").click(function() {
        var url = $("#clickable a").attr("href");
        window.location = url;
        return false;
   });
TheVillageIdiot
źródło
-3

Wszystkie rozwiązania są skomplikowane i jscript. Oto najprostsza wersja:

var IsChildWindow=false;

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


function ChildClick()
{
    IsChildWindow=true;
    //Do ur work here    
}
ratnesh
źródło
3
myślę, że popełniłeś błąd w linii „IsChildWindow == false;” - czy nie powinno to być „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
źródło
5
Zapobiega to również nawigacji łącza.
Rex M
Tak, czy nie, co jest potrzebne ?. A może musisz być znawca?
andres descalzo