Jaka jest różnica między event.stopPropagation a event.preventDefault?

833

Wydaje się, że robią to samo ...
Czy jeden jest nowoczesny, a drugi stary? Czy są obsługiwane przez różne przeglądarki?

Kiedy sam obsługuję zdarzenia (bez frameworka) zawsze po prostu sprawdzam oba i wykonuję oba, jeśli są obecne. (Ja też return false, ale mam wrażenie, że nie działa z wydarzeniami związanymi z node.addEventListener).

Dlaczego więc oba? Czy powinienem sprawdzać oba? Czy rzeczywiście jest różnica?

(Wiem, wiele pytań, ale wszystkie są takie same =))

Rudie
źródło

Odpowiedzi:

1012

stopPropagation powstrzymuje wydarzenie przed pojawieniem się łańcucha zdarzeń.

preventDefault zapobiega domyślnemu działaniu przeglądarki w przypadku tego zdarzenia.

Przykłady

preventDefault

$("#but").click(function (event) {
  event.preventDefault()
})
$("#foo").click(function () {
  alert("parent click event fired!")
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foo">
  <button id="but">button</button>
</div>

stopPropagation

$("#but").click(function (event) {
  event.stopPropagation()
})
$("#foo").click(function () {
  alert("parent click event fired!")
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foo">
  <button id="but">button</button>
</div>

Z stopPropagationtylko button's click obsługi nazywa natomiast divjest kliknięcie Wózek nigdy pożary.

Gdzie tak, jakbyś używał preventDefault, tylko domyślna akcja przeglądarki jest zatrzymywana, ale moduł obsługi kliknięć div nadal działa.

Poniżej znajdują się niektóre dokumenty dotyczące właściwości zdarzeń DOM i metod z MDN:

W przypadku IE9 i FF można po prostu użyć funkcji preventDefault & stopPropagation.

Do wspierania IE8 i obniżyć zastąpić stopPropagationz cancelBubblei wymienić preventDefaultzreturnValue

Raynos
źródło
Więc są bardzo różne? Czy IE ma do tego dwie różne metody zdarzeń? (Czy mogą być takie same?) Dziwne jest to, że frameworki event.stoppełnią obie funkcje ... Dziwne, że nigdy nie miałem z tym problemu. Często używam bulgotania. Dzięki za przykład!
Rudie
@Rudie skomentował obsługę przeglądarki.
Raynos
7
Warto zauważyć (na wypadek, gdybyś nie przeglądał dokumentów MSDN), że cancelBubblei returnValueoba są właściwościami (tak powinny być ustawione cancelBubble = true;), i że preventDefaulti stopPropagationsą metodami (tak należy nazwać preventDefault();)
freefaller
1
@Raynos, tylko jedna uwaga: event.stopPropagation () nie tylko powstrzymuje wydarzenie przed pojawieniem się łańcucha zdarzeń, ale także zatrzymuje propagację zdarzenia w fazie przechwytywania ( developer.mozilla.org/en-US/docs/Web/ API / Event / stopPropagation )
Yuci
1
Przydałoby się wiedzieć, jaka jest domyślna akcja kliknięcia przycisku, dzięki czemu mogę zrozumieć, dlaczego stopDefault()nie działa tak samo jak stopPropagation(). Jeśli domyślna akcja nie wywołuje onclickmetody, co to jest?
d512,
249

Terminologia

Z quirksmode.org :

Przechwytywanie zdarzeń

Gdy używasz przechwytywania zdarzeń

               | |
--------------- | | -----------------
| element1 | | |
| ----------- | | ----------- |
| | element2 \ / | |
| ------------------------- |
| PRZECHOWYWANIE ZDARZEŃ |
-----------------------------------

moduł obsługi zdarzeń elementu 1 uruchamia się jako pierwszy, moduł obsługi zdarzeń elementu 2 jest uruchamiany jako ostatni.

Bulgotanie zdarzeń

Gdy używasz propagacji zdarzeń

               / \
--------------- | | -----------------
| element1 | | |
| ----------- | | ----------- |
| | element2 | | | |
| ------------------------- |
| Wydarzenie BUBBLING |
-----------------------------------

moduł obsługi zdarzeń elementu 2 uruchamia się jako pierwszy, moduł obsługi zdarzeń elementu 1 uruchamia się jako ostatni.

Każde zdarzenie mające miejsce w modelu zdarzenia W3C jest najpierw rejestrowane, aż dotrze do elementu docelowego, a następnie ponownie pęka .

                 | | / \
----------------- | | - | | -----------------
| element1 | | | | |
| ------------- | | - | | ----------- |
| | element2 \ / | | | |
| -------------------------------- |
| Model zdarzenia W3C |
------------------------------------------

Berło

Z w3.org , do przechwytywania zdarzeń :

Jeśli przechwytywanie EventListenerchce zapobiec dalszemu przetwarzaniu zdarzenia, może wywołać stopPropagationmetodę Eventinterfejsu. Zapobiegnie to dalszemu wysyłaniu zdarzenia, chociaż dodatkowe osoby EventListenerszarejestrowane na tym samym poziomie hierarchii nadal będą je otrzymywać. Po wywołaniu stopPropagation metody zdarzenia kolejne wywołania tej metody nie mają dodatkowego efektu. Jeśli nie istnieją żadne dodatkowe programy przechwytujące i stopPropagationnie zostały one wywołane, zdarzenie wyzwala odpowiedni obiekt EventListenersdocelowy.

W przypadku propagacji zdarzeń :

Każdy moduł obsługi zdarzeń może uniemożliwić dalszą propagację zdarzeń, wywołując stopPropagationmetodę Eventinterfejsu. Jeśli którakolwiek z EventListenertych metod zostanie wywołana , wszystkie dodatkowe EventListenersna prądzie EventTargetzostaną wyzwolone, ale bulgotanie ustanie na tym poziomie. Tylko jeden telefon stopPropagationjest wymagany, aby zapobiec dalszemu bulgotaniu.

Aby anulować wydarzenie :

Anulowanie odbywa się przez wywołanie Event„s preventDefault metody. Jeśli jedno lub więcej EventListenerswywołań preventDefaultpodczas dowolnej fazy przepływu zdarzeń, działanie domyślne zostanie anulowane.

Przykłady

W poniższych przykładach kliknięcie hiperłącza w przeglądarce internetowej uruchamia przepływ zdarzenia (detektory zdarzeń są wykonywane) i domyślną akcję celu zdarzenia (otwiera się nowa karta).

HTML:

<div id="a">
  <a id="b" href="http://www.google.com/" target="_blank">Google</a>
</div>
<p id="c"></p>

JavaScript:

var el = document.getElementById("c");

function capturingOnClick1(ev) {
    el.innerHTML += "DIV event capture<br>";
}

function capturingOnClick2(ev) {
    el.innerHTML += "A event capture<br>";
}

function bubblingOnClick1(ev) {
    el.innerHTML += "DIV event bubbling<br>";
}

function bubblingOnClick2(ev) {
    el.innerHTML += "A event bubbling<br>";
}

// The 3rd parameter useCapture makes the event listener capturing (false by default)
document.getElementById("a").addEventListener("click", capturingOnClick1, true);
document.getElementById("b").addEventListener("click", capturingOnClick2, true);
document.getElementById("a").addEventListener("click", bubblingOnClick1, false);
document.getElementById("b").addEventListener("click", bubblingOnClick2, false);

Przykład 1 : daje wynik

DIV event capture
A event capture
A event bubbling
DIV event bubbling

Przykład 2 : dodanie stopPropagation()do funkcji

function capturingOnClick1(ev) {
    el.innerHTML += "DIV event capture<br>";
    ev.stopPropagation();
}

daje wynik

DIV event capture

Detektor zdarzeń uniemożliwił dalszą propagację zdarzenia w dół i w górę. Nie zapobiegło to jednak domyślnej akcji (otwarcie nowej karty).

Przykład 3 : dodanie stopPropagation()do funkcji

function capturingOnClick2(ev) {
    el.innerHTML += "A event capture<br>";
    ev.stopPropagation();
}

lub funkcja

function bubblingOnClick2(ev) {
    el.innerHTML += "A event bubbling<br>";
    ev.stopPropagation();
}

daje wynik

DIV event capture
A event capture
A event bubbling

Jest tak, ponieważ oba detektory zdarzeń są zarejestrowane w tym samym celu zdarzenia. Detektory zdarzeń uniemożliwiły dalszą propagację zdarzenia w górę. Nie zapobiegły one jednak domyślnej akcji (otwarcie nowej karty).

Przykład 4 : na przykład dodanie preventDefault()do dowolnej funkcji

function capturingOnClick1(ev) {
    el.innerHTML += "DIV event capture<br>";
    ev.preventDefault();
}

zapobiega otwieraniu nowej karty.

Ashkan
źródło
23
Dziękujemy za głębsze rozróżnienie między przechwytywaniem a propagacją, podczas gdy druga odpowiedź dotyczy tylko problemów związanych z jQuery.
floribon
@floribon powiedział, że nie masz zamiaru obrażać Raynosa za jego odpowiedź.
Mahi,
3
@Mahi co? Mówię tylko fakty, przyjęta odpowiedź używa jQuery, która nie jest przyjmowana przez OP, więc dla mnie nie jest to rzeczywista odpowiedź. To tylko techniczny fakt, nic osobistego i nikt się nie obraża.
floribon
2
Twoja odpowiedź jest bardzo systematyczna i ładnie pokazuje, co się dzieje. Początkowo nie chciałem ci niepokoić i samemu wprowadzać pewne zmiany w odpowiedzi, ponieważ uważam, że można to nieco poprawić w celu zwiększenia przejrzystości. Ta odpowiedź wyjaśnia model wydarzenia początkującym i dlatego uważam, że każda pomoc, jaką możemy udzielić początkującym, przechodzi długą drogę. Moja propozycja edycji została odrzucona przez ogólne argumenty, z którymi się nie zgadzam. Jeśli uważasz, @Ashkan, że te niewielkie zmiany mogą pomóc w poprawie odpowiedzi, prosimy o ich uwzględnienie.
mucaho
68

zwracać fałsz;


return false; robi 3 oddzielne rzeczy, gdy go nazwiesz:

  1. event.preventDefault() - Zatrzymuje domyślne zachowanie przeglądarki.
  2. event.stopPropagation() - Zapobiega propagowaniu zdarzenia (lub „propagowaniu”) DOM.
  3. Zatrzymuje wykonywanie oddzwaniania i zwraca natychmiast po wywołaniu

Zauważ, że to zachowanie różni się od zwykłych (nie jQuery) programów obsługi zdarzeń, w których, w szczególności, return falsenie powstrzymuje to wystąpienia zdarzenia.

preventDefault ();


preventDefault(); robi jedno: zatrzymuje domyślne zachowanie przeglądarki.

Kiedy ich używać?


Wiemy, co robią, ale kiedy ich używać? Po prostu zależy to od tego, co chcesz osiągnąć. Użyj, preventDefault();jeśli chcesz „po prostu” zapobiec domyślnemu zachowaniu przeglądarki. Użyj return false; gdy chcesz zapobiec domyślnemu zachowaniu przeglądarki i uniemożliwić propagowanie DOM przez zdarzenie. W większości sytuacji, w których użyłbyś return return; tak naprawdę chcesz preventDefault().

Przykłady:


Spróbujmy zrozumieć na przykładach:

Zobaczymy czysty przykład JAVASCRIPT

Przykład 1:

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='executeChild()'>Click here to visit stackoverflow.com</a>
</div>
<script>
  function executeChild() {
    alert('Link Clicked');
  }

  function executeParent() {
    alert('div Clicked');
  }
</script>

Uruchom powyższy kod, a zobaczysz hiperłącze „Kliknij tutaj, aby odwiedzić stackoverflow.com” teraz, jeśli najpierw klikniesz ten link, otrzymasz alert javascript Link Kliknięty Następnie otrzymasz alert javascript Kliknięty i natychmiast nastąpi przekierowanie do stackoverflow.com.

Przykład 2:

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='executeChild()'>Click here to visit stackoverflow.com</a>
</div>
<script>
  function executeChild() {
    event.preventDefault();
    event.currentTarget.innerHTML = 'Click event prevented'
    alert('Link Clicked');
  }

  function executeParent() {
    alert('div Clicked');
  }
</script>

Uruchom powyższy kod, a zobaczysz hiperłącze „Kliknij tutaj, aby odwiedzić stackoverflow.com” teraz, jeśli najpierw klikniesz ten link, otrzymasz alert javascript Link Kliknięty Następnie otrzymasz alert javascript Kliknięty Następnie zobaczysz hiperlink Kliknij tutaj, aby odwiedzić stackoverflow.com ”zastąpiony tekstem„ Zapobieganie zdarzeniom kliknięcia ”i nie zostaniesz przekierowany do stackoverflow.com. Jest to spowodowane> zastosowaną metodą event.preventDefault (), aby zapobiec wyzwalaniu domyślnej akcji kliknięcia.

Przykład 3:

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='executeChild()'>Click here to visit stackoverflow.com</a>
</div>
<script>
  function executeChild() {
    event.stopPropagation();
    event.currentTarget.innerHTML = 'Click event prevented'
    alert('Link Clicked');
  }

  function executeParent() {
    alert('div Clicked');
  }
</script>

Tym razem, jeśli klikniesz na Link, funkcja executeParent () nie zostanie wywołana i nie dostaniesz powiadomienia div javascript Kliknięty tym razem. Wynika to z tego, że zapobiegliśmy propagacji do rodzica div za pomocą metody event.stopPropagation (). Następnie zobaczysz hiperłącze „Kliknij tutaj, aby odwiedzić stackoverflow.com” zastąpione tekstem „Zdarzenie kliknięcia zostanie wykonane” i natychmiast nastąpi przekierowanie do stackoverflow.com. Wynika to z faktu, że nie zapobiegliśmy wyzwalaniu domyślnej akcji kliknięcia tym razem za pomocą metody event.preventDefault ().

Przykład 4:

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='executeChild()'>Click here to visit stackoverflow.com</a>
</div>
<script>
  function executeChild() {
    event.preventDefault();
    event.stopPropagation();
    event.currentTarget.innerHTML = 'Click event prevented'
    alert('Link Clicked');
  }

  function executeParent() {
    alert('Div Clicked');
  }
</script>

Po kliknięciu łącza funkcja executeParent () nie zostanie wywołana i nie pojawi się alert javascript. Wynika to z tego, że zapobiegliśmy propagacji do rodzica div za pomocą metody event.stopPropagation (). Następnie zobaczysz hiperłącze „Kliknij tutaj, aby odwiedzić stackoverflow.com” zastąpione tekstem „Zapobieganie zdarzeniom kliknięcia” i nie zostaniesz przekierowany do stackoverflow.com. Jest tak, ponieważ zapobiegliśmy wyzwalaniu domyślnej akcji kliknięcia tym razem za pomocą metody event.preventDefault ().

Przykład 5:

Dla zwrotu false mam trzy przykłady i wszystkie wydają się robić dokładnie to samo (po prostu zwracają false), ale w rzeczywistości wyniki są zupełnie inne. Oto, co faktycznie dzieje się w każdym z powyższych.

przypadki:

  1. Zwrócenie wartości false z wbudowanej procedury obsługi zdarzeń uniemożliwia przeglądarce przejście do adresu linku, ale nie powstrzymuje propagacji zdarzenia przez DOM.
  2. Zwrócenie wartości false z procedury obsługi zdarzeń jQuery uniemożliwia przeglądarce przejście do adresu linku i powstrzymuje propagację zdarzenia przez DOM.
  3. Zwracanie wartości false ze zwykłej procedury obsługi zdarzeń DOM absolutnie nic nie robi.

Zobaczę wszystkie trzy przykłady.

  1. Zwracany wartość false.

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='return false'>Click here to visit stackoverflow.com</a>
</div>
<script>
  var link = document.querySelector('a');

  link.addEventListener('click', function() {
    event.currentTarget.innerHTML = 'Click event prevented using inline html'
    alert('Link Clicked');
  });


  function executeParent() {
    alert('Div Clicked');
  }
</script>

  1. Zwracanie wartości false z procedury obsługi zdarzeń jQuery.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
  <a href='https://stackoverflow.com'>Click here to visit stackoverflow.com</a>
</div>
<script>
  $('a').click(function(event) {
    alert('Link Clicked');
    $('a').text('Click event prevented using return FALSE');
    $('a').contents().unwrap();
    return false;
  });
  $('div').click(function(event) {
    alert('Div clicked');
  });
</script>

  1. Zwracanie wartości false ze zwykłej procedury obsługi zdarzeń DOM.

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='executeChild()'>Click here to visit stackoverflow.com</a>
</div>
<script>
  function executeChild() {
    event.currentTarget.innerHTML = 'Click event prevented'
    alert('Link Clicked');
    return false
  }

  function executeParent() {
    alert('Div Clicked');
  }
</script>

Mam nadzieję, że te przykłady są jasne. Spróbuj wykonać wszystkie te przykłady w pliku HTML, aby zobaczyć, jak działają.

Keval Bhatt
źródło
1
Dobre przykłady, ale byłoby jeszcze lepiej bez jQuery, ponieważ jQuery fałszuje obiekt zdarzenia, więc technicznie jest inaczej.
Rudie
1
@Rudie Zaktualizowałem moją odpowiedź i usunąłem z niej jquery, a także dodałem przykład dla return false.
Keval Bhatt,
17

Oto cytat stąd

Event.preventDefault

Metoda preventDefault zapobiega realizacji domyślnej funkcji przez zdarzenie. Na przykład użyłbyś funkcji preventDefault dla elementu A, aby zatrzymać klikanie tego elementu przed opuszczeniem bieżącej strony:

//clicking the link will *not* allow the user to leave the page 
myChildElement.onclick = function(e) { 
    e.preventDefault(); 
    console.log('brick me!'); 
};

//clicking the parent node will run the following console statement because event propagation occurs
logo.parentNode.onclick = function(e) { 
    console.log('you bricked my child!'); 
};

Chociaż domyślna funkcjonalność elementu jest zamurowana, zdarzenie nadal powoduje powiększenie modelu DOM.

Event.stopPropagation

Druga metoda, stopPropagation, umożliwia domyślną funkcjonalność zdarzenia, ale uniemożliwia jego propagowanie:

//clicking the element will allow the default action to occur but propagation will be stopped...
myChildElement.onclick = function(e) { 
    e.stopPropagation();
    console.log('prop stop! no bubbles!'); 
};

//since propagation was stopped by the child element's onClick, this message will never be seen!
myChildElement.parentNode.onclick = function(e) { 
    console.log('you will never see this message!'); 
};

stopPropagation skutecznie powstrzymuje elementy nadrzędne od wiedzy o danym zdarzeniu na jego dziecku.

Chociaż prosta metoda zatrzymywania pozwala nam szybko obsługiwać zdarzenia, ważne jest, aby zastanowić się, co dokładnie chcesz zrobić z propagacją. Założę się, że wszystkim, czego naprawdę chce programista, jest zapobieganie Domyślnie w 90% przypadków! Nieprawidłowe „zatrzymanie” zdarzenia może spowodować liczne problemy na linii; Twoje wtyczki mogą nie działać, a wtyczki innych firm mogą zostać zablokowane. Albo jeszcze gorzej - Twój kod psuje inne funkcje witryny.

VladL
źródło
2

Event.preventDefault - zatrzymuje domyślne zachowanie przeglądarki. Teraz pojawia się domyślne zachowanie przeglądarki. Załóżmy, że masz tag kotwicy i ma on atrybut href, a ten tag kotwicy jest zagnieżdżony w tagu div, który ma zdarzenie click. Domyślnym zachowaniem znacznika zakotwiczenia jest kliknięcie znacznika zakotwiczenia, po którym powinien nawigować, ale to, co robi event.preventDefault, zatrzymuje nawigację w tym przypadku. Ale nigdy nie zatrzymuje bulgotania zdarzenia lub eskalacji zdarzenia, tj

<div class="container">
 <a href="#" class="element">Click Me!</a>
</div>

$('.container').on('click', function(e) {
 console.log('container was clicked');
});

$('.element').on('click', function(e) {
  e.preventDefault(); // Now link won't go anywhere
  console.log('element was clicked');
});

Wynik będzie

„element został kliknięty”

„pojemnik został kliknięty”

Teraz event.StopPropation przestaje bulgotać zdarzenie lub eskalację zdarzenia. Teraz z powyższym przykładem

$('.container').on('click', function(e) {
  console.log('container was clicked');
});

$('.element').on('click', function(e) {
  e.preventDefault(); // Now link won't go anywhere
  e.stopPropagation(); // Now the event won't bubble up
 console.log('element was clicked');
});

Wynik będzie

„element został kliknięty”

Aby uzyskać więcej informacji, skorzystaj z tego łącza https://codeplanet.io/preventdefault-vs-stoppropagation-vs-stopimmediatepropagation/

Nayas Subramanian
źródło
1

event.preventDefault(); Zatrzymuje działanie domyślnej akcji elementu.

event.stopPropagation(); Zapobiega propagacji zdarzenia w drzewie DOM, zapobiegając powiadamianiu o zdarzeniu przez wszystkie programy nadrzędne.

Na przykład, jeśli istnieje link z metodą kliknięcia dołączoną wewnątrz a DIVlub FORMktóra ma również dołączoną metodę kliknięcia, uniemożliwi to uruchomienie metody DIVlub FORMkliknięcia.

Mike Clark
źródło
-3

$("#but").click(function(event){
console.log("hello");
  event.preventDefault();
 });


$("#foo").click(function(){
 alert("parent click event fired !");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foo">
  <button id="but">button</button>
</div>

Manish Singh
źródło