Nie można zrozumieć parametru useCapture w addEventListener

290

Przeczytałem artykuł na https://developer.mozilla.org/en/DOM/element.addEventListener, ale nie mogę zrozumieć useCaptureatrybutu. Definicja to:

Jeśli true, useCapture wskazuje, że użytkownik chce zainicjować przechwytywanie. Po zainicjowaniu przechwytywania wszystkie zdarzenia określonego typu zostaną wysłane do zarejestrowanego odbiornika, zanim zostaną wysłane do dowolnych obiektów EventTargets pod nim w drzewie DOM. Wydarzenia, które bulgoczą w górę przez drzewo, nie wyzwalają słuchacza wyznaczonego do użycia przechwytywania.

W tym kodzie zdarzenie nadrzędne wyzwala się przed dzieckiem, więc nie jestem w stanie zrozumieć jego zachowania. Obiekt dokumentu ma usecapture true, a div div ma usecapture ustawiony na false, a dokument usecapture jest przestrzegany.

function load() {
  document.addEventListener("click", function() {
    alert("parent event");
  }, true);

  document.getElementById("div1").addEventListener("click", function() {
    alert("child event");
  }, false);
}
<body onload="load()">
  <div id="div1">click me</div>
</body>

użytkownik26732
źródło

Odpowiedzi:

350

Zdarzenia mogą być aktywowane dwa razy: na początku („przechwytywanie”) i na końcu („bańka”). Zdarzenia są wykonywane w kolejności ich zdefiniowania. Załóżmy, że definiujesz 4 detektory zdarzeń:

window.addEventListener("click", function(){console.log(1)}, false);
window.addEventListener("click", function(){console.log(2)}, true);
window.addEventListener("click", function(){console.log(3)}, false);
window.addEventListener("click", function(){console.log(4)}, true);

Komunikaty dziennika pojawią się w następującej kolejności:

  • 2(zdefiniowano najpierw, używając capture=true)
  • 4(zdefiniowane drugie użycie capture=true)
  • 1(pierwsze zdefiniowane zdarzenie z capture=false)
  • 3 (drugie zdefiniowane zdarzenie z capture=false )
Rob W.
źródło
49
Kolejność wykonywania jest nie gwarantowane : no specification is made as to the order in which they will receive the event with regards to the other EventListeners on the EventTarget. Nie przetestowałem wszystkich przeglądarek, więc mogą się zdarzyć, że zaimplementują to w ten sam sposób. Zdarzenia przechwytywania będą jednak wykonywane przed zdarzeniami nie przechwytywania.
beatgammit,
47
@tjameson Kolejność wykonywania jest gwarantowana w następstwie specyfikacji DOM2, zdarzeń DOM3 : „implementacja musi określać potencjalne detektory zdarzeń kandydujących na bieżący cel . Musi to być lista wszystkich detektorów zdarzeń zarejestrowanych w bieżącym celu w ich kolejność rejestracji. ”
Rob W
1
więc jest to w zasadzie związane z kolejnością zdarzeń, jak sądzę
wolniej
1
@slier, tak, kolejność wykonywania wielu procedur obsługi tego samego zdarzenia.
JMD,
6
Nie mam pojęcia, dlaczego jest to akceptowana odpowiedź, skoro afaik, przechwytywanie i propagowanie rozmów na temat zachowania propagacyjnego, a nie narzucania kolejności wykonywania wielu sąsiednim
modułom
272

Uważam, że ten schemat jest bardzo przydatny do zrozumienia faz przechwytywania / celu / bąbelków: http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Event-20031107/events.html#Events-phases

Poniżej treść pobrana z linku.

Fazy

Zdarzenie jest wywoływane zgodnie ze ścieżką od katalogu głównego drzewa do tego węzła docelowego. Następnie można go obsłużyć lokalnie na poziomie węzła docelowego lub od przodków dowolnego celu wyżej w drzewie. Wywoływanie zdarzeń (zwane także propagacją zdarzeń) odbywa się w trzech fazach i w następującej kolejności:

  1. Faza przechwytywania: zdarzenie jest wysyłane do przodków celu z katalogu głównego drzewa do bezpośredniego rodzica węzła docelowego.
  2. Faza docelowa: zdarzenie jest wysyłane do węzła docelowego.
  3. Faza propagacji: zdarzenie jest wysyłane do przodków celu od bezpośredniego rodzica węzła docelowego do korzenia drzewa.

graficzna reprezentacja zdarzenia wywołanego w drzewie DOM przy użyciu przepływu zdarzeń DOM

Przodkowie celu są określani przed początkową wysyłką zdarzenia. Jeśli węzeł docelowy zostanie usunięty podczas wysyłki lub dodany lub usunięty zostanie przodek celu, propagacja zdarzeń będzie zawsze oparta na węźle docelowym, a przodkowie celu zostaną ustaleni przed wysyłką.

Niektóre zdarzenia niekoniecznie muszą spełniać trzy fazy przepływu zdarzeń DOM, np. Zdarzenie można zdefiniować tylko dla jednej lub dwóch faz. Na przykład zdarzenia zdefiniowane w tej specyfikacji zawsze osiągną fazę przechwytywania i fazę docelową, ale niektóre nie osiągną fazy propagacji („zdarzenia propagacji” w porównaniu z „zdarzeniami niestabilnymi”, patrz także atrybut Event.bubbles).

lax4mike
źródło
1
bardzo ładny schemat!
Alex
1
A co z dziećmi węzła docelowego? Kiedy dostaną wydarzenie?
Aurimas,
Czy w rzeczywistości korzeń drzewa jest Windowzamiast document, ponieważ documentjest dzieckiem Window?
stackjlei
1
@Aurimas nie, nie ma to sensu. Cel jest najbardziej wewnętrznym elementem, który powinien otrzymać zdarzenie. Jeśli np. Klikniesz element <body> (puste miejsce), wszystkie elementy wewnątrz <body> (= wszystkie elementy strony) oczywiście nie powinny otrzymać zdarzenia click.
amik
1
Chciałbym tylko wszystkich zasobów, które wyjaśniły „co” obejmowało „dlaczego”. Udaj się do zwykłego googlowania.
aaaaaa
80

Capture Event ( useCapture = true) vs Bubble Event ( useCapture = false)

Odniesienie MDN

  • Zdarzenie przechwytywania zostanie wysłane przed wydarzeniem Bubble
  • Kolejność propagacji zdarzeń to
    1. Capture Parent
    2. Przechwytywanie dzieci
    3. Target Capture i Target Bubble
      • W kolejności, w jakiej zostały zarejestrowane
      • Gdy element jest celem zdarzenia, useCaptureparametr nie ma znaczenia (Dzięki @bam i @ legend80s)
    4. Bubble dzieci
    5. Bubble Parent
  • stopPropagation() zatrzyma przepływ

użyj przepływu przechwytywania

Próbny

Wynik:

  1. Capture Parent
  2. Bańka docelowa 1

    (Ponieważ Capture i Bubble of Target zostaną uruchomione w kolejności, w jakiej zostały zarejestrowane, więc zdarzenie Bubble zostanie uruchomione przed zdarzeniem Capture)

  3. Przechwytywanie celu

  4. Bańka docelowa 2
  5. Bubble Parent

var parent = document.getElementById('parent'),
target = document.getElementById('target');

target.addEventListener('click', function (e) { 
console.log('Target Bubble 1');
// e.stopPropagation();
}, false);

target.addEventListener('click', function (e) { 
console.log('Target Capture');
// e.stopPropagation();
}, true);

target.addEventListener('click', function (e) { 
console.log('Target Bubble 2');
// e.stopPropagation();
}, false);

parent.addEventListener('click', function (e) { 
console.log('Parent Capture');
// e.stopPropagation();
}, true);

parent.addEventListener('click', function (e) { 
console.log('Parent Bubble');
// e.stopPropagation();
}, false);
<div id="parent">
    <button id="target" style="padding: 1em 0.8em;">
        Trigger event
    </button>
</div>

Steely Wing
źródło
1
W tym przykładzie jest błąd: zadeklarowałeś zdarzenia potomne w kolejności: 1. wychwytywanie dziecka 2. bąbelek dziecka To ma znaczenie! Tylko dlatego, że Dziecko będzie celem wydarzenia, słuchacze zostaną wezwani w tej samej kolejności. Zobacz uwagę w MDN: gdy element jest celem zdarzenia, parametr „useCapture” nie ma znaczenia. ( developer.mozilla.org/en-US/docs/Web/API/EventTarget/… )
bam
1
Uwaga : W przypadku detektorów zdarzeń podłączonych do celu zdarzenia, zdarzenie jest w fazie docelowej, a nie w fazie przechwytywania i propagacji. Events in the target phase will trigger all listeners on an element in the order they were registered, regardless of the useCapture parameter.From developer.mozilla.org/en-US/docs/Web/API/EventTarget/… . Dlatego nie ma fazy „Przechwytywanie dzieci” i „Bańka dziecięca”.
legend80s
To wyjaśnia, dlaczego uruchomienie przykładu powoduje wygenerowanie „Dzieci z bańki 1” przed „Dzieci przechwytywaniem”, kiedy schemat sugeruje, że „przechwytywanie” powinno zawsze występować jako pierwsze dla dowolnego elementu!
Gershom
18

Kiedy powiesz useCapture = true, Zdarzenia wykonują się od góry do dołu w fazie przechwytywania, gdy false, powoduje to bąbelkowanie od dołu do góry.

Sushil Bharwani
źródło
11

Chodzi o modele zdarzeń: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow Możesz wychwycić zdarzenie w fazie propagacji lub przechwytywania. Twój wybór.
Spójrz na http://www.quirksmode.org/js/events_order.html - przekonasz się, że jest to bardzo przydatne.

NilColor
źródło
1
linki do w3 są tak samo lub mniej przydatne niż wyszukiwarka Google i nie mogę nic tam zrozumieć.
Muhammad Umer
3
Tak, ten link w3 jest po prostu ogromną ilością słów, ale przeciwnie, ten drugi link do strony quirksmode wyjaśnia temat bardzo dobrze i krótko.
Stano
11

Przykład kodu:

<div id="div1" style="background:#9595FF">
  Outer Div<br />
  <div id="div2" style="background:#FFFFFF">
    Inner Div
  </div>
</div>

Kod JavaScript:

d1 = document.getElementById("div1");
d2 = document.getElementById("div2");

jeśli oba są ustawione na false

d1.addEventListener('click',function(){alert("Div 1")},false);
d2.addEventListener('click',function(){alert("Div 2")},false);

Wykonuje: Onclick Wewnętrzny Div, alarmy są wyświetlane jako: Div 2> Div 1

Tutaj skrypt jest wykonywany z elementu wewnętrznego: Event Bubbling (useCapture zostało ustawione na false)

div 1 jest ustawiony na true, a div 2 na false

d1.addEventListener('click',function(){alert("Div 1")},true);
d2.addEventListener('click',function(){alert("Div 2")},false);

Wykonuje: Po kliknięciu opcji Wewnętrzna dywizja alerty są wyświetlane jako: Div 1> Div 2

Tutaj skrypt jest wykonywany z elementu nadrzędnego / zewnętrznego: Przechwytywanie zdarzeń (parametr useCapture został ustawiony na wartość true)

div 1 ma wartość false, a div 2 ma wartość true

d1.addEventListener('click',function(){alert("Div 1")},false);
d2.addEventListener('click',function(){alert("Div 2")},true);

Wykonuje: Onclick Wewnętrzny Div, alarmy są wyświetlane jako: Div 2> Div 1

Tutaj skrypt jest wykonywany z elementu wewnętrznego: Event Bubbling (useCapture zostało ustawione na false)

div 1 jest ustawiony na true, a div 2 ustawiony na true

d1.addEventListener('click',function(){alert("Div 1")},true);
d2.addEventListener('click',function(){alert("Div 2")},true);

Wykonuje: Po kliknięciu opcji Wewnętrzna dywizja alerty są wyświetlane jako: Div 1> Div 2

Tutaj skrypt jest wykonywany z elementu nadrzędnego / zewnętrznego: Przechwytywanie zdarzeń, ponieważ parametr useCapture został ustawiony na wartość true

shadowBot
źródło
1
Jakie znaczenie mają w tym kontekście „większe niż” szewrony?
2540625
9

Podsumowanie:

DOMSpec jak opisano w:

https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

działa w następujący sposób:

Zdarzenie jest wywoływane po ścieżce od katalogu głównego ( document) drzewa do węzła docelowego . Węzeł docelowy jest najgłębszym HTMLelementem, tj. Event.target. Wywoływanie zdarzeń (zwane także propagacją zdarzeń) odbywa się w trzech fazach i w następującej kolejności:

  1. Faza przechwytywania: zdarzenie jest wysyłane do przodków celu z katalogu głównego drzewa ( document) do bezpośredniego rodzica węzła docelowego.
  2. Faza docelowa: zdarzenie jest wysyłane do węzła docelowego. Faza docelowa jest zawsze najgłębszym htmlelementem, na którym wydarzenie zostało odrzucone.
  3. Faza propagacji: zdarzenie jest wysyłane do przodków celu od bezpośredniego rodzica węzła docelowego do korzenia drzewa.

Bubbing zdarzeń, przechwytywanie zdarzeń, cel zdarzenia

Przykład:

// bubbling handlers, third argument (useCapture) false (default)
document.getElementById('outerBubble').addEventListener('click', () => {
  console.log('outerBubble');
}, false)

document.getElementById('innerBubble').addEventListener('click', () => {
  console.log('innerBubble');
}, false)


// capturing handlers, third argument (useCapture)  true
document.getElementById('outerCapture').addEventListener('click', () => {
  console.log('outerCapture');
}, true)

document.getElementById('innerCapture').addEventListener('click', () => {
  console.log('innerCapture');
}, true)
div:hover{
  color: red;
  cursor: pointer;
}
<!-- event bubbling -->
<div id="outerBubble">
  <div id="innerBubble">click me to see Bubbling</div>
</div>


<!-- event capturing -->
<div id="outerCapture">
  <div id="innerCapture">click me to see Capturing</div>
</div>

Powyższy przykład naprawdę ilustruje różnicę między propagacją zdarzeń a przechwytywaniem zdarzeń. Podczas dodawania detektorów zdarzeń za pomocą addEventListenerjest trzeci element o nazwie useCapture. To ustawienie, booleanktóre po ustawieniu truepozwala odbiornikowi zdarzenia na użycie przechwytywania zdarzeń zamiast propagacji zdarzeń.

W naszym przykładzie, gdy ustawiamy argument useCapture na false, widzimy, że następuje propagacja zdarzeń. Najpierw uruchamiane jest zdarzenie w fazie docelowej (logi innerBubble), a następnie poprzez bąbelkowanie zdarzenia uruchamiane jest zdarzenie w elemencie nadrzędnym (logi outerBubble).

Kiedy ustawimy argument useCapture, aby truezobaczyć, że zdarzenie w zewnętrznej pamięci <div>jest uruchamiane jako pierwsze. Jest tak, ponieważ zdarzenie jest teraz odpalane w fazie przechwytywania, a nie w fazie bulgotania.

Willem van der Veen
źródło
7

Biorąc pod uwagę trzy fazy podróży wydarzenia :

  1. Faza przechwytywania : zdarzenie jest wysyłane do przodków celu z katalogu głównego drzewa do bezpośredniego rodzica węzła docelowego.
  2. Faza docelowa : zdarzenie jest wysyłane do węzła docelowego.
  3. Faza propagacji : zdarzenie jest wysyłane do przodków celu od bezpośredniego rodzica węzła docelowego do korzenia drzewa.

useCapturewskazuje, na których fazach będzie odbywać się podróż zdarzenia :

Jeśli true, useCapture wskazuje, że użytkownik chce dodać detektor zdarzeń tylko dla fazy przechwytywania, tzn. Ten detektor zdarzeń nie zostanie wyzwolony podczas faz celu i propagacji. Jeśli falsedetektor zdarzeń zostanie aktywowany tylko podczas fazy celu i propagacji

Źródło jest takie samo jak druga najlepsza odpowiedź: https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Event-20031107/events.html#Events-phases

Aurimas
źródło
2

Kolejność definicji ma znaczenie tylko wtedy, gdy elementy są na tym samym poziomie. Jeśli odwrócisz kolejność definicji w kodzie, otrzymasz takie same wyniki.

Jeśli jednak odwrócisz ustawienie useCapture na dwóch modułach obsługi zdarzeń, moduł obsługi zdarzeń podrzędnych zareaguje przed ustawieniem elementu nadrzędnego. Powodem tego jest to, że procedura obsługi zdarzeń potomnych będzie teraz wyzwalana w fazie przechwytywania, która jest przed fazą propagacji, w której wyzwalana jest procedura obsługi zdarzeń nadrzędnych.

Jeśli ustawisz wartość useCapture na wartość true dla obu procedur obsługi zdarzeń - niezależnie od kolejności definicji - funkcja obsługi zdarzeń nadrzędnych zostanie uruchomiona jako pierwsza, ponieważ występuje ona przed dzieckiem w fazie przechwytywania.

I odwrotnie, jeśli ustawisz useCapture na false dla obu procedur obsługi zdarzeń - ponownie bez względu na kolejność definicji - procedura obsługi zdarzeń podrzędnych zostanie uruchomiona jako pierwsza, ponieważ jest ona nadrzędna w fazie propagacji.

WXB13
źródło