Impossible de comprendre le paramètre useCapture dans addEventListener

290

J'ai lu l'article sur https://developer.mozilla.org/en/DOM/element.addEventListener mais je n'arrive pas à comprendre l' useCaptureattribut. Définition il y a:

Si la valeur est true, useCapture indique que l'utilisateur souhaite lancer la capture. Après avoir lancé la capture, tous les événements du type spécifié seront distribués à l'écouteur enregistré avant d'être distribués aux EventTargets situés en dessous dans l'arborescence DOM. Les événements qui remontent dans l'arborescence ne déclencheront pas d'auditeur désigné pour utiliser la capture.

Dans ce code, l'événement parent se déclenche avant l'enfant, donc je ne suis pas en mesure de comprendre son comportement.

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>

user26732
la source

Réponses:

350

Les événements peuvent être activés à deux reprises: au début ("capture") et à la fin ("bulle"). Les événements sont exécutés dans l'ordre de leur définition. Dites, vous définissez 4 écouteurs d'événements:

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

Les messages du journal apparaîtront dans cet ordre:

  • 2(défini en premier, en utilisant capture=true)
  • 4(deuxième défini à l'aide capture=true)
  • 1(premier événement défini avec capture=false)
  • 3(deuxième événement défini avec capture=false)
Rob W
la source
49
Ordre d'exécution est pas garantie : no specification is made as to the order in which they will receive the event with regards to the other EventListeners on the EventTarget. Je n'ai pas testé tous les navigateurs, il se peut donc que tous les implémentent de la même manière. Les événements de capture seront cependant effectués avant les événements non capturés.
beatgammit
47
@tjameson L'ordre d'exécution est garanti dans le successeur de la spécification DOM2, événements DOM3 : "l'implémentation doit déterminer les écouteurs d'événements candidats de la cible actuelle . Il doit s'agir de la liste de tous les écouteurs d'événements qui ont été enregistrés sur la cible actuelle dans leur ordre d'enregistrement. "
Rob W
1
donc cela a essentiellement à voir avec l'ordre des événements, je suppose
slier
1
@slier, oui, l'ordre dans lequel plusieurs gestionnaires pour le même événement sont exécutés.
JMD
6
Aucune idée pourquoi c'est la réponse acceptée depuis afaik, la capture et le bouillonnement parlent du comportement de propagation et non de dicter l'ordre d'exécution pour plusieurs gestionnaires d'événements adjacents
georaldc
272

Je trouve que ce diagramme est très utile pour comprendre les phases de capture / cible / bulle: http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

Ci-dessous, le contenu extrait du lien.

Phases

L'événement est distribué en suivant un chemin depuis la racine de l'arborescence vers ce nœud cible. Il peut ensuite être géré localement au niveau du nœud cible ou à partir des ancêtres de n'importe quelle cible plus haut dans l'arborescence. La répartition des événements (également appelée propagation d'événements) se déroule en trois phases et dans l'ordre suivant:

  1. La phase de capture: l'événement est envoyé aux ancêtres de la cible de la racine de l'arbre au parent direct du nœud cible.
  2. La phase cible: l'événement est envoyé au nœud cible.
  3. La phase bouillonnante: l'événement est distribué aux ancêtres de la cible du parent direct du nœud cible à la racine de l'arborescence.

représentation graphique d'un événement distribué dans une arborescence DOM à l'aide du flux d'événements DOM

Les ancêtres de la cible sont déterminés avant l'envoi initial de l'événement. Si le nœud cible est supprimé lors de la répartition ou si l'ancêtre d'une cible est ajouté ou supprimé, la propagation de l'événement sera toujours basée sur le nœud cible et les ancêtres de la cible déterminés avant la répartition.

Certains événements peuvent ne pas nécessairement accomplir les trois phases du flux d'événements DOM, par exemple l'événement ne peut être défini que pour une ou deux phases. Par exemple, les événements définis dans cette spécification accompliront toujours les phases de capture et cible mais certains n'accompliront pas la phase de propagation ("événements de propagation" contre "événements sans propagation", voir également l'attribut Event.bubbles).

lax4mike
la source
1
très joli diagramme!
Alex
1
Qu'en est-il des enfants du nœud cible? Quand reçoivent-ils l'événement?
Aurimas
La racine de l'arbre est-elle réellement Window, au lieu de document, parce que documentest un enfant de Window?
stackjlei
1
@Aurimas non, cela n'aurait aucun sens. La cible est l'élément le plus interne qui devrait recevoir l'événement. Si vous cliquez sur l'élément <body> (un endroit vide), tous les éléments à l'intérieur de <body> (= tous les éléments de la page) ne devraient évidemment pas recevoir l'événement click.
amik
1
Je souhaite juste toutes les ressources qui ont expliqué le "quoi" comprenait un "pourquoi". En route pour googler comme d'habitude.
aaaaaa
80

Événement de capture ( useCapture = true) vs Événement de bulle ( useCapture = false)

Référence MDN

  • L'événement de capture sera envoyé avant l'événement de bulle
  • L'ordre de propagation des événements est
    1. Capture parent
    2. Capture d'enfants
    3. Capture cible et bulle cible
      • Dans l'ordre où ils ont été enregistrés
      • Lorsque l'élément est la cible de l'événement, le useCaptureparamètre n'a pas d'importance (merci @bam et @ legend80s)
    4. Bulle d'enfants
    5. Bulle parent
  • stopPropagation() arrêtera le flux

utiliser le flux de capture

Démo

Résultat:

  1. Capture parent
  2. Bulle cible 1

    (Parce que Capture et Bubble of Target se déclencheront dans l'ordre dans lequel ils ont été enregistrés, donc l'événement Bubble est déclenché avant l'événement Capture)

  3. Capture de cible

  4. Cible Bubble 2
  5. Bulle 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>

Aile d'acier
la source
1
Il y a une erreur dans l'exemple: vous avez déclaré les événements enfants dans l'ordre: 1. capture enfant 2. bulle enfant C'est important! Tout simplement parce que si l'enfant sera la cible de l'événement, les auditeurs seront appelés dans le même ordre. Voir la note sur MDN: lorsque l'élément est la cible de l'événement, le paramètre 'useCapture' n'a pas d'importance. ( developer.mozilla.org/en-US/docs/Web/API/EventTarget/… )
bam
1
Remarque : pour les écouteurs d'événement attachés à la cible d'événement, l'événement se trouve dans la phase cible, plutôt que dans les phases de capture et de propagation. Events in the target phase will trigger all listeners on an element in the order they were registered, regardless of the useCapture parameter.Depuis developer.mozilla.org/en-US/docs/Web/API/EventTarget/… . Il n'y a donc pas de phase de "Capture d'Enfants" et de "Bulle d'Enfants".
legend80s
Et cela explique pourquoi l'exécution de l'exemple produit "Bulle d'enfants 1" avant "Capture d'enfants", lorsque le diagramme suggère que la "capture" devrait toujours se produire en premier pour tout élément!
Gershom
18

Lorsque vous dites useCapture = true, les événements s'exécutent de haut en bas dans la phase de capture lorsque false, cela fait une bulle de bas en haut.

sushil bharwani
la source
11

Il s'agit de modèles d'événements: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow Vous pouvez capturer un événement en phase de propagation ou en phase de capture. Votre choix.
Jetez un œil à http://www.quirksmode.org/js/events_order.html - vous le trouverez très utile.

NilColor
la source
1
les liens vers w3 sont aussi utiles, voire moins utiles, que la recherche google, je n'y comprends rien.
Muhammad Umer
3
Oui, ce lien w3 n'est qu'un énorme tas de mots, mais en face de lui, ce deuxième lien vers le site quirksmode explique très bien et brièvement le sujet.
Stano
11

Exemple de code:

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

Code Javascript:

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

si les deux sont définis sur false

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

Exécute: Onclicking Inner Div, les alertes sont affichées comme: Div 2> Div 1

Ici, le script est exécuté à partir de l'élément interne: Event Bubbling (useCapture a été défini sur false)

div 1 est défini sur true et div 2 sur false

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

Exécute: Onclicking Inner Div, les alertes sont affichées comme: Div 1> Div 2

Ici, le script est exécuté à partir de l'élément ancêtre / externe: Capture d'événement (useCapture a été défini sur true)

div 1 est défini sur false et div 2 sur true

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

Exécute: Onclicking Inner Div, les alertes sont affichées comme: Div 2> Div 1

Ici, le script est exécuté à partir de l'élément interne: Event Bubbling (useCapture a été défini sur false)

div 1 est défini sur true et div 2 sur true

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

Exécute: Onclicking Inner Div, les alertes sont affichées comme: Div 1> Div 2

Ici, le script est exécuté à partir de l'élément ancêtre / externe: Capture d'événement depuis que useCapture a été défini sur true

shadowBot
la source
1
Quelle est la signification des chevrons "supérieurs à" dans ce contexte?
2540625 du
9

Résumé:

La DOMspécification décrite dans:

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

fonctionne de la manière suivante:

Un événement est distribué suivant un chemin depuis la racine ( document) de l'arborescence jusqu'au nœud cible . Le nœud cible est l' HTMLélément le plus profond , c'est-à-dire le event.target. La répartition des événements (également appelée propagation d'événements) se déroule en trois phases et dans l'ordre suivant:

  1. La phase de capture: l'événement est distribué aux ancêtres de la cible de la racine de l'arbre ( document) au parent direct du nœud cible.
  2. La phase cible: l'événement est envoyé au nœud cible. La phase cible est toujours sur l' htmlélément le plus profond sur lequel l'événement a été diffusé.
  3. La phase bouillonnante: l'événement est envoyé aux ancêtres de la cible du parent direct du nœud cible à la racine de l'arbre.

Bulle d'événements, capture d'événements, cible d'événements

Exemple:

// 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'exemple ci-dessus illustre vraiment la différence entre la propagation d'événements et la capture d'événements. Lors de l'ajout des écouteurs d'événement avec addEventListener, il existe un troisième élément appelé useCapture. Ceci, booleanqui, lorsqu'il est défini sur, truepermet à l'écouteur d'événements d'utiliser la capture d'événements au lieu de la propagation d'événements.

Dans notre exemple, lorsque nous définissons l'argument useCapture sur, falsenous voyons que la propagation d' événements a lieu. Tout d'abord, l'événement de la phase cible est déclenché (enregistre innerBubble), puis via un bullage d'événement, l'événement dans l'élément parent est déclenché (enregistre externalBubble).

Lorsque nous définissons l'argument useCapture sur truenous voyons que l'événement à l'extérieur <div>est déclenché en premier. En effet, l'événement est maintenant déclenché dans la phase de capture et non dans la phase de propagation.

Willem van der Veen
la source
7

Compte tenu des trois phases du voyage événementiel :

  1. La phase de capture : l'événement est envoyé aux ancêtres de la cible de la racine de l'arbre au parent direct du nœud cible.
  2. La phase cible : l'événement est envoyé au nœud cible.
  3. La phase bouillonnante : l'événement est distribué aux ancêtres de la cible du parent direct du nœud cible à la racine de l'arbre.

useCaptureindique pour quelles phases l'événement Voyage sera sur:

Si true, useCapture indique que l'utilisateur souhaite ajouter l'écouteur d'événements pour la phase de capture uniquement, c'est-à-dire que cet écouteur d'événements ne sera pas déclenché pendant les phases cible et de propagation. Si false, l'écouteur d'événements ne sera déclenché que pendant les phases cible et de propagation

La source est la même que la deuxième meilleure réponse: https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

Aurimas
la source
2

L'ordre de définition n'a d'importance que si les éléments sont au même niveau. Si vous inversez l'ordre de définition dans votre code, vous obtiendrez les mêmes résultats.

Toutefois, si vous inversez le paramètre useCapture sur les deux gestionnaires d'événements, le gestionnaire d'événements enfant répond avant celui du parent. La raison en est que le gestionnaire d'événements enfant sera désormais déclenché dans la phase de capture qui est antérieure à la phase de propagation dans laquelle le gestionnaire d'événements parent sera déclenché.

Si vous définissez useCapture sur true pour les deux gestionnaires d'événements - quel que soit l'ordre de définition - le gestionnaire d'événements parent sera déclenché en premier car il précède l'enfant dans la phase de capture.

Inversement, si vous définissez useCapture sur false pour les deux gestionnaires d'événements - à nouveau quel que soit l'ordre de définition - le gestionnaire d'événements enfant sera déclenché en premier car il précède le parent dans la phase de propagation.

WXB13
la source