Der useCapture-Parameter in addEventListener kann nicht verstanden werden


290

Ich habe den Artikel unter https://developer.mozilla.org/en/DOM/element.addEventListener gelesen, kann aber das useCaptureAttribut nicht verstehen . Definition gibt es:

Wenn true, gibt useCapture an, dass der Benutzer die Erfassung starten möchte. Nach dem Starten der Erfassung werden alle Ereignisse des angegebenen Typs an den registrierten Listener gesendet, bevor sie an EventTargets darunter in der DOM-Struktur gesendet werden. Ereignisse, die durch den Baum nach oben sprudeln, lösen keinen Listener aus, der für die Verwendung der Erfassung vorgesehen ist.

In diesem Code wird das übergeordnete Ereignis vor dem untergeordneten Ereignis ausgelöst, sodass ich sein Verhalten nicht verstehen kann. Das Dokumentobjekt hat usecapture true und child div hat usecapture auf false gesetzt und document usecapture wird befolgt. Deshalb wird die document-Eigenschaft gegenüber child bevorzugt.

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>

Antworten:


350

Ereignisse können zweimal aktiviert werden: Am Anfang ("Capture") und am Ende ("Bubble"). Ereignisse werden in der Reihenfolge ausgeführt, in der sie definiert sind. Angenommen, Sie definieren 4 Ereignis-Listener:

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

Die Protokollmeldungen werden in dieser Reihenfolge angezeigt:

  • 2(zuerst definiert mit capture=true)
  • 4(zweite definiert mit capture=true)
  • 1(erstes definiertes Ereignis mit capture=false)
  • 3(zweites definiertes Ereignis mit capture=false)

49
Die Reihenfolge der Ausführung kann nicht garantiert werden : no specification is made as to the order in which they will receive the event with regards to the other EventListeners on the EventTarget. Ich habe nicht alle Browser getestet, daher können sie alle auf die gleiche Weise implementiert werden. Erfassungsereignisse werden jedoch vor nicht erfassten Ereignissen durchgeführt.
Beatgammit

47
@tjameson Die Ausführungsreihenfolge wird im Nachfolger der DOM2-Spezifikation DOM3-Ereignisse garantiert : "Die Implementierung muss die Kandidaten-Ereignis-Listener des aktuellen Ziels bestimmen . Dies muss die Liste aller Ereignis-Listener sein, die auf dem aktuellen Ziel in ihrem registriert wurden Reihenfolge der Registrierung. "
Rob W

1
Das hat also im Grunde mit der Reihenfolge der Ereignisse zu tun, denke ich
slier

1
@slier, ja, die Reihenfolge, in der mehrere Handler für dasselbe Ereignis ausgeführt werden.
JMD

6
Keine Ahnung, warum dies die akzeptierte Antwort ist, da afaik beim Erfassen und Sprudeln über das Ausbreitungsverhalten spricht und nicht darüber, die Ausführungsreihenfolge für mehrere benachbarte Ereignishandler zu diktieren
georaldc

272

Ich finde dieses Diagramm sehr nützlich, um die Erfassungs- / Ziel- / Blasenphasen zu verstehen: http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

Unten wird der Inhalt aus dem Link extrahiert.

Phasen

Das Ereignis wird über einen Pfad von der Wurzel des Baums zu diesem Zielknoten ausgelöst. Es kann dann lokal auf der Ebene des Zielknotens oder von den Vorfahren eines Ziels weiter oben im Baum verarbeitet werden. Die Ereignisverteilung (auch Ereignisausbreitung genannt) erfolgt in drei Phasen und in der folgenden Reihenfolge:

  1. Die Erfassungsphase: Das Ereignis wird von der Wurzel des Baums an die direkten Eltern des Zielknotens an die Vorfahren des Ziels gesendet.
  2. Die Zielphase: Das Ereignis wird an den Zielknoten gesendet.
  3. Die Blasenphase: Das Ereignis wird vom direkten übergeordneten Element des Zielknotens an die Vorfahren des Ziels an die Wurzel des Baums gesendet.

Grafische Darstellung eines Ereignisses, das in einem DOM-Baum mithilfe des DOM-Ereignisflusses ausgelöst wird

Die Vorfahren des Ziels werden vor dem ersten Versand des Ereignisses ermittelt. Wenn der Zielknoten während des Versands entfernt wird oder der Vorfahr eines Ziels hinzugefügt oder entfernt wird, basiert die Ereignisausbreitung immer auf dem Zielknoten und den Vorfahren des Ziels, die vor dem Versand ermittelt wurden.

Einige Ereignisse erfüllen möglicherweise nicht unbedingt die drei Phasen des DOM-Ereignisflusses, z. B. kann das Ereignis nur für eine oder zwei Phasen definiert werden. Beispielsweise führen Ereignisse, die in dieser Spezifikation definiert sind, immer die Erfassungs- und Zielphase durch, einige jedoch nicht die Blasenphase ("Sprudelereignisse" im Vergleich zu "nicht sprudelnden Ereignissen", siehe auch das Attribut "Event.bubbles").


1
sehr schönes Diagramm!
Alex

1
Wie wäre es mit den Kindern des Zielknotens? Wann bekommen sie die Veranstaltung?
Aurimas

Ist die Wurzel des Baumes tatsächlich Windowstatt document, weil documentist ein Kind von Window?
Stackjlei

Dieses Diagramm hat sehr geholfen. Danke !
Thomas An

1
Ich wünsche mir nur alle Ressourcen, die das "Was" erklärt haben, einschließlich eines "Warum". Auf zum Googeln wie immer.
aaaaaa

80

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

MDN-Referenz

  • Das Erfassungsereignis wird vor dem Blasenereignis versendet
  • Ereignisausbreitungsreihenfolge ist
    1. Übergeordnete Erfassung
    2. Kinder erfassen
    3. Zielerfassung und Zielblase
      • In der Reihenfolge wurden sie registriert
      • Wenn das Element das Ziel des Ereignisses ist, spielt der useCaptureParameter keine Rolle (Danke @bam und @ legend80s)
    4. Kinderblase
    5. Elternblase
  • stopPropagation() stoppt den Fluss

Verwenden Sie Capture Flow

Demo

Ergebnis:

  1. Übergeordnete Erfassung
  2. Zielblase 1

    (Da Capture und Bubble of Target in der Reihenfolge ihrer Registrierung ausgelöst werden, wird das Bubble-Ereignis vor dem Capture-Ereignis ausgelöst.)

  3. Zielerfassung

  4. Zielblase 2
  5. Elternblase

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>


1
Im Beispiel ist ein Fehler aufgetreten: Sie haben untergeordnete Ereignisse in der folgenden Reihenfolge deklariert: 1. Kindererfassung 2. Kinderblase Es ist wichtig! Nur weil, wenn das Kind das Ziel des Ereignisses sein wird, werden die Zuhörer in derselben Reihenfolge angerufen. Siehe den Hinweis bei MDN: Wenn das Element das Ziel des Ereignisses ist, spielt der Parameter 'useCapture' keine Rolle. ( developer.mozilla.org/en-US/docs/Web/API/EventTarget/… )
bam

1
Hinweis : Für Ereignis-Listener, die an das Ereignisziel angehängt sind, befindet sich das Ereignis in der Zielphase und nicht in der Erfassungs- und Blasenphase. Events in the target phase will trigger all listeners on an element in the order they were registered, regardless of the useCapture parameter.Von developer.mozilla.org/en-US/docs/Web/API/EventTarget/… . Es gibt also keine Phase von "Children Capture" und "Children Bubble".
legend80s

Und das erklärt, warum das Ausführen des Beispiels "Kinderblase 1" vor "Kindererfassung" erzeugt, wenn das Diagramm vorschlägt, dass "Erfassung" für jedes Element immer zuerst erfolgen sollte!
Gershom

18

Wenn Sie useCapture = true sagen, werden die Ereignisse in der Erfassungsphase von oben nach unten ausgeführt, wenn false, wird eine Blase von unten nach oben ausgeführt.


11

Alles dreht sich um Ereignismodelle: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow Sie können Ereignisse in der Blasen- oder Erfassungsphase erfassen. Deine Entscheidung.
Werfen Sie einen Blick auf http://www.quirksmode.org/js/events_order.html - Sie werden es sehr nützlich finden.


1
Links zu w3 sind genauso oder sogar weniger nützlich als die Google-Suche. Ich kann dort nichts verstehen.
Muhammad Umer

3
Ja, dieser w3-Link ist nur eine riesige Ansammlung von Wörtern, aber im Gegensatz dazu erklärt dieser zweite Link zur Quirksmode- Site das Thema sehr gut und kurz.
Stano

11

Codebeispiel:

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

Javascript-Code:

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

wenn beide auf false gesetzt sind

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

Wird ausgeführt: Wenn Sie auf Inner Div klicken, werden Warnungen wie folgt angezeigt: Div 2> Div 1

Hier wird das Skript aus dem inneren Element ausgeführt: Event Bubbling (useCapture wurde auf false gesetzt)

div 1 wird auf true und div 2 auf false gesetzt

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

Wird ausgeführt: Wenn Sie auf Inner Div klicken, werden Warnungen wie folgt angezeigt: Div 1> Div 2

Hier wird das Skript vom Vorfahren / äußeren Element ausgeführt: Ereigniserfassung (useCapture wurde auf true gesetzt)

div 1 wird auf false und div 2 auf true gesetzt

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

Wird ausgeführt: Wenn Sie auf Inner Div klicken, werden Warnungen wie folgt angezeigt: Div 2> Div 1

Hier wird das Skript aus dem inneren Element ausgeführt: Event Bubbling (useCapture wurde auf false gesetzt)

div 1 wird auf true und div 2 auf true gesetzt

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

Wird ausgeführt: Wenn Sie auf Inner Div klicken, werden Warnungen wie folgt angezeigt: Div 1> Div 2

Hier wird das Skript vom Vorfahren / äußeren Element ausgeführt: Ereigniserfassung, da useCapture auf true gesetzt wurde


1
Was bedeuten die Chevrons "größer als" in diesem Zusammenhang?
2540625

9

Zusammenfassung:

Die DOMin:

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

funktioniert folgendermaßen:

Ein Ereignis wird auf einem Pfad von der Wurzel ( document) des Baums zum Zielknoten ausgelöst . Der Zielknoten ist das tiefste HTMLElement, dh das event.target. Die Ereignisverteilung (auch Ereignisausbreitung genannt) erfolgt in drei Phasen und in der folgenden Reihenfolge:

  1. Die Erfassungsphase: Das Ereignis wird von der Wurzel des Baums ( document) an die Vorfahren des Ziels an das direkte übergeordnete Element des Zielknotens gesendet.
  2. Die Zielphase: Das Ereignis wird an den Zielknoten gesendet. Die htmlZielphase befindet sich immer auf dem tiefsten Element, auf dem das Ereignis ausgelöst wurde.
  3. Die Blasenphase: Das Ereignis wird vom direkten übergeordneten Element des Zielknotens an die Vorfahren des Ziels an die Wurzel des Baums gesendet.

Ereignisblasen, Ereigniserfassung, Ereignisziel

Beispiel:

// 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>

Das obige Beispiel zeigt wirklich den Unterschied zwischen Ereignisblasenbildung und Ereigniserfassung. Beim Hinzufügen der Ereignis-Listener mit addEventListenergibt es ein drittes Element namens useCapture. Dies ist eine booleanOption, bei trueder der Ereignis-Listener die Ereigniserfassung anstelle des Ereignisblasen verwenden kann.

In unserem Beispiel sehen wir, wenn wir das Argument useCapture auf setzen false, dass ein Ereignis sprudelt. Zuerst wird das Ereignis in der Zielphase ausgelöst (protokolliert innerBubble), und dann wird über Ereignisblasen das Ereignis im übergeordneten Element ausgelöst (protokolliert OuterBubble).

Wenn wir das Argument useCapture auf setzen true, sehen wir, dass das Ereignis im äußeren <div>zuerst ausgelöst wird. Dies liegt daran, dass das Ereignis jetzt in der Erfassungsphase und nicht in der Blasenphase ausgelöst wird.


7

Angesichts der drei Phasen der Veranstaltung Reise :

  1. Die Erfassungsphase : Das Ereignis wird von der Wurzel des Baums an die direkten Eltern des Zielknotens an die Vorfahren des Ziels gesendet.
  2. Die Zielphase : Das Ereignis wird an den Zielknoten gesendet.
  3. Die Bubbling-Phase : Das Ereignis wird vom direkten übergeordneten Element des Zielknotens an die Vorfahren des Ziels an die Wurzel des Baums gesendet.

useCapturezeigt an, für die Phasen der Veranstaltung Reise wird auf:

Wenn true, useCapture zeigen an, dass der Benutzer wünscht , den Ereignis - Listener für die Erfassungsphase hinzuzufügen, dh nur dieses Ereignis Hörer wird nicht während der Ziel- und Bubbling - Phase ausgelöst werden. In falsediesem Fall wird der Ereignis-Listener nur während der Ziel- und Blasenphase ausgelöst

Die Quelle entspricht der zweitbesten Antwort: https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases


2

Die Reihenfolge der Definition ist nur wichtig, wenn sich die Elemente auf derselben Ebene befinden. Wenn Sie die Reihenfolge der Definition in Ihrem Code umkehren, erhalten Sie die gleichen Ergebnisse.

Wenn Sie jedoch die Einstellung useCapture für die beiden Ereignishandler umkehren, antwortet der untergeordnete Ereignishandler vor dem des übergeordneten Ereignisses. Der Grund dafür ist, dass der untergeordnete Ereignishandler jetzt in der Erfassungsphase ausgelöst wird, die vor der Blasenphase liegt, in der der übergeordnete Ereignishandler ausgelöst wird.

Wenn Sie useCapture für beide Ereignishandler auf true setzen - unabhängig von der Reihenfolge der Definition - wird der übergeordnete Ereignishandler zuerst ausgelöst, da er in der Erfassungsphase vor dem untergeordneten Element liegt.

Wenn Sie dagegen useCapture für beide Ereignishandler auf false setzen - wiederum unabhängig von der Reihenfolge der Definition -, wird der untergeordnete Ereignishandler zuerst ausgelöst, da er in der Bubbling-Phase vor dem übergeordneten Element steht.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.