Невозможно понять параметр useCapture в addEventListener

Я прочитал статью в https://developer.mozilla.org/en/DOM/element.addEventListener, но не смог понять атрибут useCapture. Определение есть:

Если true, useCapture указывает, что пользователь хочет инициировать захват. После начала захвата все события указанного типа будут отправлены зарегистрированному прослушивателю перед отправкой на любые EventTargets под ним в дереве DOM. События, которые пузырятся вверх через дерево, не будут вызывать слушателя, назначенного для использования захвата.

В этом родительском событии кода триггеры перед дочерним, поэтому я не могу понять его поведение. Объект документа имеет usecapture true, а дочерний div имеет значение usecapture set false, а documentecapture - follow.So, почему свойство document предпочтительнее дочернего.

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>

Ответы

Ответ 1

События могут быть активированы в двух случаях: в начале ("захват") и в конце ("пузырь").     События выполняются в порядке их определения. Скажем, вы определяете 4 слушателя событий:

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

Ответ 2

Я нахожу, что эта диаграмма очень полезна для понимания фаз захвата/цели/пузырька: http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

Ниже, содержимое, извлеченное из ссылки.

Phases

Событие отправляется по пути от корня дерева к этой цели node. Затем его можно обрабатывать локально на целевом уровне node или от любых целевых предков, выше в дереве. Рассылка событий (также называемая размножением событий) происходит в три этапа и следующий порядок:

  • Фаза захвата: событие отправляется целевым предкам от корня дерева до прямого родителя целевой node.
  • Целевая фаза: событие отправляется в цель node.
  • Фаза барботирования: событие отправляется в цель предков от прямого родителя мишени node до корня дерево.

graphical representation of an event dispatched in a DOM tree using the DOM event flow

Целевые предки определяются перед начальной отправкой события. Если целевой объект node удаляется во время диспетчеризации или добавляется или удаляется целевой предок, распространение события всегда будет основываться на целевом node и целевых предках, определенных перед отправкой.

Некоторые события могут не обязательно выполнять три фазы потока событий DOM, например. событие может быть определено только для одной или двух фаз. В качестве примера события, определенные в этой спецификации, всегда будут выполнять фазы захвата и цели, но некоторые из них не будут выполнять этап барботирования ( "события барботирования" по сравнению с "событиями без пузырьков", см. Также атрибут Event.bubbles).

Ответ 3

Событие захвата (useCapture = true) против события Bubble (useCapture = false)

MDN Ссылка

  • Событие захвата будет отправлено до Bubble Event.
  • Порядок распространения событий
    1. Родительский захват
    2. Захват детей
    3. Целевой захват и целевой пузырь
      • В порядке их регистрации
      • Когда элемент является целью события, параметр useCapture не имеет значения (спасибо @bam и @legend80s)
    4. Детский пузырь
    5. Родительский пузырь
  • stopPropagation() остановит поток

use Capture flow

демонстрация

Результат:

  1. Родительский захват
  2. Детский пузырь 1
  3. Захват детей

    (Поскольку "Цель" - "Дети", "Захват" и "Пузырь" сработают в том порядке, в котором они были зарегистрированы).

  4. Детский пузырь 2
  5. Родительский пузырь

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

children.addEventListener('click', function (e) { 
    alert('Children Bubble 1');
    // e.stopPropagation();
}, false);

children.addEventListener('click', function (e) { 
    alert('Children Capture');
    // e.stopPropagation();
}, true);

children.addEventListener('click', function (e) { 
    alert('Children Bubble 2');
    // e.stopPropagation();
}, false);

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

parent.addEventListener('click', function (e) { 
    alert('Parent Bubble');
    // e.stopPropagation();
}, false);
<div id="parent">
    <div id="children">
        Click
    </div>
</div>

Ответ 4

Когда вы говорите, что useCapture = true, события выполняются сверху вниз в фазе захвата, когда false, это делает верхнюю часть пузырька вверху.

Ответ 5

Пример кода:

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

Код Javascript:

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

если для обоих установлено значение false

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

Выполняет: Onclicking Inner Div, предупреждения отображаются как:  Div 2 > Div 1

Здесь script выполняется из внутреннего элемента: Event Bubbling (useCapture имеет значение false)

div 1 установлен в true, а div 2 - false

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

Выполняет: Onclicking Inner Div, предупреждения отображаются как:  Div 1 > Div 2

Здесь script выполняется из предка/внешнего элемента: Event Capturing (useCapture имеет значение true)

div 1 установлен в значение false, а div 2 - true

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

Выполняет: Onclicking Inner Div, предупреждения отображаются как:  Div 2 > Div 1

Здесь script выполняется из внутреннего элемента: Event Bubbling (useCapture имеет значение false)

div 1 устанавливается в true, а div 2 - true

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

Выполняет: Onclicking Inner Div, предупреждения отображаются как:  Div 1 > Div 2

Здесь script выполняется из предка/внешнего элемента: Event Capturing, поскольку useCapture имеет значение true

Ответ 7

Учитывая три этапа событийного путешествия:

  1. Фаза захвата: событие отправляется целевым предкам от корня дерева до прямого родителя целевого узла.
  2. Целевая фаза: событие отправляется на целевой узел.
  3. Фаза пузырьков: событие отправляется целевым предкам от прямого родителя целевого узла к корню дерева.

useCapture указывает, для каких фаз будет происходить перемещение события:

Если true, useCapture указывает, что пользователь желает добавить прослушиватель событий только для фазы захвата, то есть этот прослушиватель событий не будет запущен во время целевых и пузырьковых фаз. Если false, прослушиватель событий будет срабатывать только во время целевого и пузырькового этапов.

Источник совпадает со вторым лучшим ответом: https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

Ответ 8

Резюме:

Спецификация DOM описана в:

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

работает следующим образом:

Событие отправляется по пути от корня (document) дерева к целевому узлу. Целевой узел - это самый глубокий HTML элемент, то есть event.target. Распределение событий (также называемое распространением событий) происходит в три этапа и в следующем порядке:

  1. Фаза захвата: событие отправляется целевым предкам от корня дерева (document) до прямого родителя целевого узла.
  2. Целевая фаза: событие отправляется на целевой узел. Целевая фаза всегда находится на самом глубоком элементе html для которого было отправлено событие.
  3. Фаза пузырьков: событие отправляется целевым предкам от прямого родителя целевого узла к корню дерева.

Event bubbling, event capturing, event target

Пример:

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

Ответ 9

Порядок определения имеет значение только в том случае, если элементы находятся на одном уровне. Если вы измените порядок определения в своем коде, вы получите те же результаты.

Однако, если вы измените параметр useCapture на двух обработчиках событий, обработчик дочерних событий отвечает перед родителем. Причина этого в том, что обработчик дочерних событий теперь будет запущен на этапе захвата, который предшествует фазе барботажа, в котором будет запускаться обработчик родительского события.

Если вы установите для useCapture значение true для обоих обработчиков событий - независимо от порядка определения - родительский обработчик событий будет запущен первым, потому что он приходит перед дочерним элементом на этапе захвата.

И наоборот, если вы установите useCapture в false для обоих обработчиков событий - снова независимо от порядка определения - обработчик дочерних событий будет запускаться первым, потому что он приходит перед родителем в фазе барботажа.