Impossibile comprendere il parametro useCapture in addEventListener


290

Ho letto l'articolo su https://developer.mozilla.org/en/DOM/element.addEventListener ma non riesco a capire l' useCaptureattributo. Definizione c'è:

Se vero, useCapture indica che l'utente desidera avviare l'acquisizione. Dopo aver avviato l'acquisizione, tutti gli eventi del tipo specificato verranno inviati al listener registrato prima di essere inviati a qualsiasi EventTarget sotto di esso nella struttura DOM. Gli eventi che gorgogliano verso l'alto attraverso l'albero non attiveranno un ascoltatore designato a utilizzare l'acquisizione.

In questo codice l'evento genitore si attiva prima del figlio, quindi non sono in grado di comprenderne il comportamento. L'oggetto documento ha usecapture true e il div child ha usecapture impostato su false e viene seguita usecapture del documento. Pertanto, perché la proprietà document è preferita rispetto a child.

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>

Risposte:


350

Gli eventi possono essere attivati ​​in due occasioni: all'inizio ("cattura") e alla fine ("bolla"). Gli eventi vengono eseguiti nell'ordine in cui sono stati definiti. Supponiamo che tu definisca 4 ascoltatori di eventi:

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

I messaggi di registro appariranno in questo ordine:

  • 2(definito per primo, usando capture=true)
  • 4(definito secondo usando capture=true)
  • 1(primo evento definito con capture=false)
  • 3(secondo evento definito con capture=false)

49
Ordine di esecuzione è non garantita : no specification is made as to the order in which they will receive the event with regards to the other EventListeners on the EventTarget. Non ho testato tutti i browser, quindi potrebbe capitare che tutti lo implementino allo stesso modo. Tuttavia, gli eventi di acquisizione verranno eseguiti prima degli eventi non di acquisizione.
beatgammit,

47
@tjameson L'ordine di esecuzione è garantito nel successore della specifica DOM2, eventi DOM3 : "l'implementazione deve determinare i listener di eventi candidati del target corrente . Questo deve essere l'elenco di tutti i listener di eventi registrati sul target corrente nel loro ordine di registrazione ".
Rob W,

1
quindi questo è fondamentalmente ha a che fare con la fine dell'evento credo
slier

1
@slier, yes, l'ordine in cui vengono eseguiti più gestori per lo stesso evento.
JMD

6
Non ho idea del perché questa sia la risposta accettata da quando afaik, catturando e facendo chiacchiere parla del comportamento di propagazione e non del dettare l'ordine di esecuzione per più gestori di eventi adiacenti
georaldc,

272

Trovo che questo diagramma sia molto utile per comprendere le fasi di acquisizione / destinazione / bolla: http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

Di seguito, il contenuto estratto dal collegamento.

fasi

L'evento viene inviato seguendo un percorso dalla radice dell'albero a questo nodo di destinazione. Può quindi essere gestito localmente a livello di nodo di destinazione o da qualsiasi antenato di destinazione più in alto nella struttura. L'invio di eventi (chiamato anche propagazione di eventi) avviene in tre fasi e nel seguente ordine:

  1. La fase di acquisizione: l'evento viene inviato agli antenati del bersaglio dalla radice dell'albero al genitore diretto del nodo bersaglio.
  2. La fase target: l'evento viene inviato al nodo target.
  3. La fase gorgogliante: l'evento viene inviato agli antenati del bersaglio dal genitore diretto del nodo bersaglio alla radice dell'albero.

rappresentazione grafica di un evento inviato in un albero DOM utilizzando il flusso di eventi DOM

Gli antenati del bersaglio sono determinati prima dell'invio iniziale dell'evento. Se il nodo di destinazione viene rimosso durante il dispacciamento o viene aggiunto o rimosso l'antenato di una destinazione, la propagazione dell'evento sarà sempre basata sul nodo di destinazione e gli antenati della destinazione determinati prima della spedizione.

Alcuni eventi potrebbero non necessariamente compiere le tre fasi del flusso di eventi DOM, ad esempio l'evento potrebbe essere definito solo per una o due fasi. Ad esempio, gli eventi definiti in questa specifica eseguiranno sempre le fasi di acquisizione e destinazione, ma alcuni non realizzeranno la fase di bubbling ("eventi di bubbling" contro "eventi non di bubbling", vedere anche l'attributo Event.bubbles).


1
diagramma molto bello!
Alex,

1
E i figli del nodo target? Quando ricevono l'evento?
Aurimas,

La radice dell'albero è effettivamente Window, invece di document, perché documentè figlia di Window?
Stackjlei,

1
@Aurimas no, non avrebbe senso. Il target è l'elemento più interno che dovrebbe ricevere l'evento. Se fai clic sull'elemento <body> (un posto vuoto), ovviamente tutti gli elementi all'interno di <body> (= tutti gli elementi della pagina) non dovrebbero ricevere l'evento click.
amik,

1
Vorrei solo che tutte le risorse che spiegavano il "cosa" includessero un "perché". Via a più googling come al solito.
aaaaaa,

80

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

Riferimento MDN

  • L'evento di acquisizione verrà inviato prima dell'evento Bubble
  • L'ordine di propagazione degli eventi è
    1. Cattura genitori
    2. Cattura bambini
    3. Target Capture e Target Bubble
      • Nell'ordine sono stati registrati
      • Quando l'elemento è il bersaglio dell'evento, il useCaptureparametro non ha importanza (grazie @bam e @ legend80s)
    4. Bolla per bambini
    5. Bolla dei genitori
  • stopPropagation() fermerà il flusso

utilizzare il flusso di acquisizione

dimostrazione

Risultato:

  1. Cattura genitori
  2. Bolla target 1

    (Poiché Capture e Bubble of Target si innescheranno nell'ordine in cui sono stati registrati, quindi l'evento Bubble viene attivato prima dell'evento Capture)

  3. Cattura del bersaglio

  4. Bolla bersaglio 2
  5. Bolla dei genitori

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
C'è un errore nell'esempio: hai dichiarato eventi secondari nell'ordine: 1. cattura dei bambini 2. bolla dei bambini È importante! Solo perché se il bambino sarà il bersaglio dell'evento, gli ascoltatori saranno chiamati nello stesso ordine. Vedi la nota su MDN: quando l'elemento è la destinazione dell'evento il parametro 'useCapture' non ha importanza. ( developer.mozilla.org/en-US/docs/Web/API/EventTarget/… )
bam

1
Nota : per i listener di eventi collegati al target dell'evento, l'evento si trova nella fase target, piuttosto che nelle fasi di acquisizione e bolle. Events in the target phase will trigger all listeners on an element in the order they were registered, regardless of the useCapture parameter.Da developer.mozilla.org/en-US/docs/Web/API/EventTarget/… . Quindi non esiste alcuna fase di "Cattura bambini" e "Bolla bambini".
Legend80s

E questo spiega perché l'esecuzione dell'esempio produce "Bolla bambini 1" prima di "Acquisizione bambini", quando il diagramma suggerisce che "acquisizione" dovrebbe sempre verificarsi prima per qualsiasi elemento!
Gershom,

18

Quando dici useCapture = true, gli eventi vengono eseguiti dall'alto verso il basso nella fase di acquisizione quando falso fa una bolla dal basso verso l'alto.


11

Si tratta di modelli di eventi: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow È possibile catturare eventi in fase di bolle o in fase di acquisizione. La tua scelta.
Dai un'occhiata a http://www.quirksmode.org/js/events_order.html : lo troverai molto utile.


1
i collegamenti a W3 sono come o anche meno utili della ricerca di Google non riesco a capire nulla lì.
Muhammad Umer l'

3
Sì, quel link w3 è solo un enorme mucchio di parole, ma al contrario di esso, quel secondo link al sito in modalità quirks spiega l'argomento molto bene e brevemente.
Stano,

11

Esempio di codice:

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

Codice Javascript:

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

se entrambi sono impostati su false

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

Esegue: Onclicking Inner Div, gli avvisi vengono visualizzati come: Div 2> Div 1

Qui lo script viene eseguito dall'elemento interno: Event Bubbling (useCapture è stato impostato su false)

div 1 è impostato su true e div 2 impostato su false

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

Esegue: Onclicking Inner Div, gli avvisi vengono visualizzati come: Div 1> Div 2

Qui lo script viene eseguito dall'antenato / elemento esterno: Event Capturing (useCapture è stato impostato su true)

div 1 è impostato su false e div 2 impostato su true

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

Esegue: Onclicking Inner Div, gli avvisi vengono visualizzati come: Div 2> Div 1

Qui lo script viene eseguito dall'elemento interno: Event Bubbling (useCapture è stato impostato su false)

div 1 è impostato su true e div 2 impostato su true

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

Esegue: Onclicking Inner Div, gli avvisi vengono visualizzati come: Div 1> Div 2

Qui lo script viene eseguito dall'antenato / elemento esterno: Event Capturing poiché useCapture è stato impostato su true


1
Qual è il significato dei galloni "maggiori di" in questo contesto?
2540625,

9

Sommario:

Le DOMspecifiche descritte in:

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

funziona nel modo seguente:

Un evento viene inviato seguendo un percorso dalla radice ( document) dell'albero al nodo di destinazione . Il nodo target è l' HTMLelemento più profondo , ovvero event.target. L'invio di eventi (chiamato anche propagazione di eventi) avviene in tre fasi e nel seguente ordine:

  1. La fase di acquisizione: l'evento viene inviato agli antenati del bersaglio dalla radice dell'albero ( document) al genitore diretto del nodo bersaglio.
  2. La fase target: l'evento viene inviato al nodo target. La fase target è sempre l' htmlelemento più profondo su cui l'evento è stato staccato.
  3. La fase gorgogliante: l'evento viene inviato agli antenati del bersaglio dal genitore diretto del nodo bersaglio alla radice dell'albero.

Bollicine di eventi, acquisizione di eventi, target di eventi

Esempio:

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

L'esempio sopra illustra davvero la differenza tra il bubbling di eventi e la cattura di eventi. Quando si aggiungono i listener di eventi con addEventListener, c'è un terzo elemento chiamato useCapture. Questo a booleanche, quando impostato su, trueconsente all'ascoltatore di eventi di utilizzare la cattura degli eventi anziché il bubbling degli eventi.

Nel nostro esempio quando impostiamo l'argomento useCapture su falsevediamo che avviene il bubbling di eventi. Innanzitutto viene generato l'evento nella fase target (registri innerBubble), quindi tramite l'evento gorgogliante viene generato l'evento nell'elemento padre (registri outerBubble).

Quando impostiamo l'argomento useCapture su truevediamo che l'evento nell'esterno <div>viene generato per primo. Questo perché l'evento viene ora generato nella fase di acquisizione e non nella fase di bubbling.


7

Date le tre fasi del viaggio dell'evento :

  1. La fase di acquisizione : l'evento viene inviato agli antenati del bersaglio dalla radice dell'albero al genitore diretto del nodo bersaglio.
  2. La fase target : l'evento viene inviato al nodo target.
  3. La fase gorgogliante : l'evento viene inviato agli antenati del bersaglio dal genitore diretto del nodo bersaglio alla radice dell'albero.

useCaptureindica per quali fasi si svolgerà il viaggio dell'evento :

Se true, useCapture indica che l'utente desidera aggiungere il listener di eventi per la fase di cattura, cioè l'ascoltatore evento non viene attivato durante la porta e frizzante fasi. In falsetal caso, il listener di eventi verrà attivato solo durante le fasi target e bubbling

La fonte è la stessa della seconda migliore risposta: https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases


2

L'ordine di definizione è importante solo se gli articoli sono allo stesso livello. Se inverti l'ordine di definizione nel tuo codice otterrai gli stessi risultati.

Tuttavia, se si inverte l'impostazione useCapture sui due gestori di eventi, il gestore di eventi figlio risponde prima di quello del padre. La ragione di ciò è che il gestore di eventi figlio verrà ora attivato nella fase di acquisizione che è precedente alla fase di bubbling in cui verrà attivato il gestore di eventi padre.

Se si imposta useCapture su true per entrambi i gestori di eventi, indipendentemente dall'ordine di definizione, il gestore di eventi padre verrà attivato per primo perché precede il figlio nella fase di acquisizione.

Al contrario, se si imposta useCapture su false per entrambi i gestori di eventi, sempre a prescindere dall'ordine di definizione, il gestore di eventi figlio verrà attivato per primo perché precede il genitore nella fase di bubbling.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.