No se puede entender el parámetro useCapture en addEventListener


290

He leído el artículo en https://developer.mozilla.org/en/DOM/element.addEventListener pero no puedo entender el useCaptureatributo. Definición hay:

Si es verdadero, useCapture indica que el usuario desea iniciar la captura. Después de iniciar la captura, todos los eventos del tipo especificado serán enviados al oyente registrado antes de ser enviados a cualquier EventTargets debajo de él en el árbol DOM. Los eventos que están burbujeando hacia arriba a través del árbol no activarán un oyente designado para usar captura.

En este código, el evento padre se dispara antes que el niño, por lo que no puedo entender su comportamiento.

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>

Respuestas:


350

Los eventos se pueden activar en dos ocasiones: al principio ("captura") y al final ("burbuja"). Los eventos se ejecutan en el orden de cómo se definen. Digamos, usted define 4 oyentes de eventos:

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

Los mensajes de registro aparecerán en este orden:

  • 2(definido primero, usando capture=true)
  • 4(definido segundo usando capture=true)
  • 1(primer evento definido con capture=false)
  • 3(segundo evento definido con capture=false)

49
Orden de ejecución se no se garantiza : no specification is made as to the order in which they will receive the event with regards to the other EventListeners on the EventTarget. No he probado todos los navegadores, por lo que es posible que todos lo implementen de la misma manera. Sin embargo, los eventos de captura se realizarán antes de los eventos sin captura.
beatgammit

47
@tjameson El orden de ejecución está garantizado en el sucesor de la especificación DOM2, eventos DOM3 : "la implementación debe determinar los oyentes de eventos candidatos del objetivo actual . Esta debe ser la lista de todos los oyentes de eventos que se han registrado en el objetivo actual en su orden de registro ".
Rob W

1
así que, básicamente, esto tiene que ver con el orden de los eventos, supongo
ligero

1
@slier, sí, el orden en que se ejecutan varios controladores para el mismo evento.
JMD

66
No tengo idea de por qué esta es la respuesta aceptada ya que afaik, capturar y burbujear habla sobre el comportamiento de propagación y no sobre dictar el orden de ejecución para los controladores de eventos múltiples adyacentes
georaldc

272

Creo que este diagrama es muy útil para comprender las fases de captura / objetivo / burbuja: http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

A continuación, contenido extraído del enlace.

Etapas

El evento se distribuye siguiendo una ruta desde la raíz del árbol a este nodo de destino. Luego se puede manejar localmente a nivel de nodo objetivo o desde los antepasados ​​de cualquier objetivo más alto en el árbol. El despacho de eventos (también llamado propagación de eventos) ocurre en tres fases y en el siguiente orden:

  1. La fase de captura: el evento se envía a los antepasados ​​del objetivo desde la raíz del árbol al padre directo del nodo objetivo.
  2. La fase de destino: el evento se envía al nodo de destino.
  3. La fase de burbujeo: el evento se envía a los antepasados ​​del objetivo desde el padre directo del nodo de destino hasta la raíz del árbol.

Representación gráfica de un evento enviado en un árbol DOM utilizando el flujo de eventos DOM

Los antepasados ​​del objetivo se determinan antes del envío inicial del evento. Si el nodo objetivo se elimina durante el envío, o si se agrega o elimina el ancestro de un objetivo, la propagación del evento siempre se basará en el nodo objetivo y los ancestros del objetivo determinados antes del envío.

Algunos eventos pueden no cumplir necesariamente las tres fases del flujo de eventos DOM, por ejemplo, el evento solo puede definirse para una o dos fases. Como ejemplo, los eventos definidos en esta especificación siempre lograrán las fases de captura y destino, pero algunos no lograrán la fase de burbujeo ("eventos de burbujeo" versus "eventos de no burbujeo", vea también el atributo Event.bubbles).


1
muy bonito diagrama!
Alex

1
¿Qué hay de los hijos del nodo objetivo? ¿Cuándo reciben el evento?
Aurimas

¿Es la raíz del árbol en realidad Window, en lugar de document, porque documentes hija de Window?
stackjlei

1
@Aurimas no lo hacen, no tendría sentido. El objetivo es el elemento más interno que debería recibir el evento. Si hace clic en el elemento <body> (un lugar vacío), todos los elementos dentro de <body> (= todos los elementos de la página) obviamente no deberían recibir el evento click.
amik

1
Solo deseo todos los recursos que explican el "qué", incluido un "por qué". A más googlear como siempre.
aaaaaa

80

Evento de captura ( useCapture = true) vs Evento de burbuja ( useCapture = false)

Referencia de MDN

  • El evento de captura se enviará antes del evento de burbuja
  • El orden de propagación de eventos es
    1. Captura de padres
    2. Captura de niños
    3. Target Capture y Target Bubble
      • En el orden en que fueron registrados
      • Cuando el elemento es el objetivo del evento, el useCaptureparámetro no importa (Gracias @bam y @ legend80s)
    4. Burbuja de los niños
    5. Burbuja de los padres
  • stopPropagation() detendrá el flujo

utilizar el flujo de captura

Manifestación

Resultado:

  1. Captura de padres
  2. Target Bubble 1

    (Debido a que Capture y Bubble of Target se activarán en el orden en que se registraron, el evento Bubble se activa antes del evento Capture)

  3. Captura objetivo

  4. Target Bubble 2
  5. Burbuja de los padres

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
Hay un error en el ejemplo: declaró eventos secundarios en el orden: 1. captura secundaria 2. burbuja secundaria ¡Es importante! Solo porque si el Niño será el objetivo del evento, los oyentes serán llamados en el mismo orden. Vea la nota en MDN: cuando el elemento es el objetivo del evento, el parámetro 'useCapture' no importa. ( developer.mozilla.org/en-US/docs/Web/API/EventTarget/… )
bam

1
Nota : Para los oyentes de eventos conectados al objetivo del evento, el evento se encuentra en la fase de destino, en lugar de las fases de captura y burbujeo. Events in the target phase will trigger all listeners on an element in the order they were registered, regardless of the useCapture parameter.De developer.mozilla.org/en-US/docs/Web/API/EventTarget/… . Por lo tanto, no existe una fase de "Captura de niños" y "Burbuja de niños".
legend80s

Y eso explica por qué ejecutar el ejemplo produce "Burbuja 1 de niños" antes de "Captura de niños", cuando el diagrama sugiere que "captura" siempre debe ocurrir primero para cualquier elemento.
Gershom

18

Cuando dice useCapture = true, los eventos se ejecutan de arriba a abajo en la fase de captura cuando es falso, hace una burbuja de abajo hacia arriba.


11

Se trata de modelos de eventos: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow Puede capturar eventos en la fase de burbujeo o en la fase de captura. Tu elección.
Eche un vistazo a http://www.quirksmode.org/js/events_order.html , lo encontrará muy útil.


1
los enlaces a w3 son tan o incluso menos útiles que la búsqueda de google, no puedo entender nada allí.
Muhammad Umer

3
Sí, ese enlace w3 es solo un montón de palabras, pero al contrario, ese segundo enlace al sitio quirksmode explica el tema muy bien y brevemente.
Stano

11

Ejemplo de código:

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

Código Javascript:

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

si ambos se establecen en falso

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

Ejecuta: Al hacer clic en Div interno, las alertas se muestran como: Div 2> Div 1

Aquí el script se ejecuta desde el elemento interno: Event Bubbling (useCapture se ha establecido en falso)

div 1 se establece en verdadero y div 2 se establece en falso

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

Ejecuta: Al hacer clic en Div interno, las alertas se muestran como: Div 1> Div 2

Aquí el script se ejecuta desde el elemento ancestro / externo: Captura de eventos (useCapture se ha establecido en verdadero)

div 1 se establece en falso y div 2 se establece en verdadero

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

Ejecuta: Al hacer clic en Div interno, las alertas se muestran como: Div 2> Div 1

Aquí el script se ejecuta desde el elemento interno: Event Bubbling (useCapture se ha establecido en falso)

div 1 se establece en verdadero y div 2 se establece en verdadero

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

Ejecuta: Al hacer clic en Div interno, las alertas se muestran como: Div 1> Div 2

Aquí el script se ejecuta desde el elemento ancestro / externo: Captura de eventos ya que useCapture se ha establecido en verdadero


1
¿Cuál es el significado de los galones "mayores que" en este contexto?
2540625

9

Resumen:

La DOMespecificación descrita en:

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

funciona de la siguiente manera:

Se distribuye un evento siguiendo una ruta desde la raíz ( document) del árbol hasta el nodo de destino . El nodo de destino es el HTMLelemento más profundo , es decir, el event.target. El despacho de eventos (también llamado propagación de eventos) ocurre en tres fases y en el siguiente orden:

  1. La fase de captura: el evento se envía a los antepasados ​​del objetivo desde la raíz del árbol ( document) al padre directo del nodo de destino.
  2. La fase de destino: el evento se envía al nodo de destino. La fase objetivo siempre está en el htmlelemento más profundo en el que se disparó el evento.
  3. La fase de burbujeo: el evento se envía a los antepasados ​​del objetivo desde el padre directo del nodo de destino hasta la raíz del árbol.

Evento burbujeante, captura de eventos, objetivo del evento

Ejemplo:

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

El ejemplo anterior realmente ilustra la diferencia entre el burbujeo de eventos y la captura de eventos. Al agregar los oyentes de eventos con addEventListener, hay un tercer elemento llamado useCapture. Esto se booleanestablece cuando truepermite que el oyente de eventos use la captura de eventos en lugar del burbujeo de eventos.

En nuestro ejemplo, cuando configuramos el argumento useCapture false, vemos que tiene lugar un burbujeo de eventos. Primero se dispara el evento en la fase de destino (registra innerBubble), y luego a través del evento que burbujea se dispara el evento en el elemento primario (registra outsideBubble).

Cuando establecemos el argumento useCapture true, vemos que el evento en el exterior <div>se dispara primero. Esto se debe a que el evento ahora se dispara en la fase de captura y no en la fase de burbujeo.


7

Dadas las tres fases del viaje del evento :

  1. La fase de captura : el evento se envía a los antepasados ​​del objetivo desde la raíz del árbol al padre directo del nodo objetivo.
  2. La fase de destino : el evento se envía al nodo de destino.
  3. La fase de burbujeo : el evento se envía a los antepasados ​​del objetivo desde el padre directo del nodo de destino hasta la raíz del árbol.

useCaptureindica para qué fases estará el viaje del evento :

Si true, useCapture indica que el usuario desea agregar el escucha de eventos solo para la fase de captura, es decir, este escucha de eventos no se activará durante las fases objetivo y burbujeante. Si false, el detector de eventos solo se activará durante las fases objetivo y burbujeante

La fuente es la misma que la segunda mejor respuesta: https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases


2

El orden de definición solo importa si los artículos están al mismo nivel. Si invierte el orden de definición en su código obtendrá los mismos resultados.

Sin embargo, si invierte la configuración useCapture en los dos controladores de eventos, el controlador de eventos secundario responde antes que el padre. La razón de esto es que el controlador de eventos hijo ahora se activará en la fase de captura que es anterior a la fase de burbujeo en la que se activará el controlador de eventos padre.

Si establece useCapture en true para ambos manejadores de eventos, independientemente del orden de definición, el manejador de eventos padre se activará primero porque está antes que el hijo en la fase de captura.

Por el contrario, si configura useCapture en falso para ambos manejadores de eventos, nuevamente independientemente del orden de definición, el manejador de eventos hijo se activará primero porque viene antes que el padre en la fase de propagación.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.