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

290

Я прочитал статью на 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>

user26732
источник

Ответы:

350

События могут быть активированы в двух случаях: в начале («захват») и в конце («пузырь»). События выполняются в порядке их определения. Скажем, вы определяете 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(определяется сначала, используя capture=true)
  • 4(определено второе использование capture=true)
  • 1(первое определенное событие с capture=false)
  • 3(второе определенное событие с capture=false)
Роб W
источник
49
Порядок выполнения не гарантируется : no specification is made as to the order in which they will receive the event with regards to the other EventListeners on the EventTarget. Я не тестировал все браузеры, поэтому все они могут реализовать его одинаково. Однако события захвата будут выполняться до событий без захвата.
beatgammit
47
@tjameson Порядок выполнения будет гарантирован в правопреемнике спецификации DOM2, DOM3 событие : «реализация должна определить текущий целевые объект слушателей кандидат событий Это должно быть списком всех слушателей событий , которые были зарегистрированы на текущей цели в их. порядок оформления. "
Роб Вт
1
так что это в основном связано с порядком событий, я думаю
Slier
1
@slier, да, порядок выполнения нескольких обработчиков для одного и того же события.
JMD
6
Понятия не имею, почему это принятый ответ, поскольку afaik, захваты и пузыри говорят о поведении распространения, а не о том, чтобы диктовать порядок выполнения для нескольких смежных обработчиков событий
georaldc
272

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

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

Этапы

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

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

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

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

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

lax4mike
источник
1
очень хорошая схема!
Алекс
1
Как насчет детей целевого узла? Когда они получают событие?
Ауримас
Является ли корень дерева фактически Window, а не documentпотому, что documentявляется потомком Window?
stackjlei
1
@ Ауримы нет, это не имеет смысла. Цель - это самый внутренний элемент, который должен получить событие. То есть, если вы нажмете на элемент <body> (пустое место), все элементы внутри <body> (= все элементы страницы), очевидно, не должны получать событие click.
амик
1
Я просто хочу, чтобы все ресурсы, которые объясняли «что», включали «почему». В поисках новых поисков, как обычно.
аааааа
80

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

Ссылка MDN

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

использовать захват потока

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

Результат:

  1. Захват родителей
  2. Целевой пузырь 1

    (Поскольку Capture и Bubble of Target будут срабатывать в том порядке, в котором они были зарегистрированы, поэтому событие Bubble запускается до события Capture)

  3. Захват цели

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

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>

Steely Wing
источник
1
В примере есть ошибка: вы объявили дочерние события в следующем порядке: 1. захват детей 2. дочерний пузырь Это важно! Просто потому, что если Child будет целью события, слушатели будут вызываться в том же порядке. Смотрите примечание в MDN: когда элемент является целью события, параметр 'useCapture' не имеет значения. ( Developer.mozilla.org/en-US/docs/Web/API/EventTarget/... )
бац
1
Примечание . Для прослушивателей событий, прикрепленных к цели события, событие находится в целевой фазе, а не в фазах захвата и всплытия. Events in the target phase will trigger all listeners on an element in the order they were registered, regardless of the useCapture parameter.От developer.mozilla.org/en-US/docs/Web/API/EventTarget/… . Таким образом, не существует фазы «Детский захват» и «Детский пузырь».
легенды80
И это объясняет, почему при выполнении примера выводится «Пузырь детей 1» перед «Захватом детей», когда диаграмма показывает, что «захват» должен всегда происходить первым для любого элемента!
Гершом
18

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

сушил бхарвани
источник
11

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

NilColor
источник
1
ссылки на w3 так же или даже менее полезны, чем поиск в Google, я ничего не могу понять там.
Мухаммед Умер
3
Да, эта ссылка на w3 - просто огромная куча слов, но, напротив, вторая ссылка на сайт quirksmode объясняет тему очень хорошо и кратко.
Стано
11

Пример кода:

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

shadowBot
источник
1
Что означает «больше чем» шевроны в этом контексте?
2540625
9

Резюме:

Спецификация 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. Фаза пузырьков: событие отправляется предкам цели от прямого родителя целевого узла к корню дерева.

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

Пример:

// 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>запускается первым. Это происходит потому, что событие теперь запускается в фазе захвата, а не в фазе барботирования.

Виллем ван дер Веен
источник
7

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

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

useCaptureуказывает на какие фазы события путешествия будут:

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

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

Aurimas
источник
2

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

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

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

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

WXB13
источник