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

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

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

В этом коде родительское событие инициируется перед дочерним, поэтому я не могу понять его поведение. Объект документа имеет usecapture true, а дочерний div имеет usecapture, установленный в false, и документ usecapture следует. Поэтому свойство документа предпочтительнее, чем 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>

9 ответов

Решение

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

window.addEventListener("click", function(){alert(1)}, false);
window.addEventListener("click", function(){alert(2)}, true);
window.addEventListener("click", function(){alert(3)}, false);
window.addEventListener("click", function(){alert(4)}, true);

Окна предупреждений появятся в следующем порядке:

  • 2 (определяется сначала, используя capture=true)
  • 4 (определено второе использование capture=true)
  • 1 (первое определенное событие с capture=false)
  • 3 (второе определенное событие с capture=false)

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

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

Этапы

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

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

графическое представление события, отправляемого в дереве DOM с использованием потока событий DOM

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

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

Событие захвата против события пузыря

  • Событие захвата будет отправлено до Bubble Event.
  • Порядок распространения событий
    1. Родительский захват
    2. Захват детей
    3. Детский пузырь
    4. Родительский пузырь

(stopPropagation() остановит поток)

                  |  A
 -----------------|--|-----------------
 | Parent         |  |                |
 |   -------------|--|-----------     |
 |   |Children    V  |          |     |
 |   ----------------------------     |
 |                                    |
 --------------------------------------

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

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

child.addEventListener('click', function(e){ 
    console.log('Child Capture, with capture');
    // e.stopPropagation();
}, true);

child.addEventListener('click', function(e){ 
    console.log('Child Bubble');
    // e.stopPropagation();
}, false);

parent.addEventListener('click', function(e){ 
    console.log('Parent Capture, with capture');
    // e.stopPropagation();
}, true);

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

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

Резюме:

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

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

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

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

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

Бублинг событий, захват событий, цель события

Пример:

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

Приведенный выше пример действительно иллюстрирует разницу между всплытием событий и захватом событий. При добавлении слушателей события с addEventListener есть третий элемент с именем useCapture. Это boolean который когда установлен true позволяет слушателю событий использовать захват событий вместо всплытия событий.

В нашем примере, когда мы устанавливаем аргумент useCapture в false мы видим, что происходит пузырение событий. Сначала запускается событие на целевой фазе (регистрирует innerBubble), а затем с помощью пузырькового события генерируется событие в родительском элементе (регистрируется externalBubble).

Когда мы устанавливаем аргумент useCapture в true мы видим, что событие во внешнем <div> уволен первым. Это происходит потому, что событие теперь происходит в фазе захвата, а не в фазе барботирования.

Это все о моделях событий: http://www.w3.org/TR/DOM-Level-2-Events/events.html Вы можете поймать событие в фазе пузырьков или в фазе захвата. Твой выбор.
Взгляните на http://www.quirksmode.org/js/events_order.html - вы найдете это очень полезным.

Пример кода:

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

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

Здесь скрипт выполняется из внутреннего элемента: 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);

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

Здесь скрипт выполняется из предка / внешнего элемента: Захват событий (useCapture было установлено в true)

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

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

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

Здесь скрипт выполняется из внутреннего элемента: 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);

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам