Невозможно понять параметр 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
Ниже содержание извлечено по ссылке.
Этапы
Событие отправляется по пути от корня дерева до этого целевого узла. Затем он может обрабатываться локально на уровне целевого узла или от любых предков цели выше в дереве. Распределение событий (также называемое распространением событий) происходит в три этапа и в следующем порядке:
- Фаза захвата: событие отправляется предкам цели от корня дерева до прямого родителя целевого узла.
- Целевая фаза: событие отправляется на целевой узел.
- Фаза пузырьков: событие отправляется предкам цели от прямого родителя целевого узла к корню дерева.
Предки цели определяются до первоначальной отправки события. Если целевой узел удаляется во время диспетчеризации, или предок цели добавляется или удаляется, распространение события всегда будет основываться на целевом узле и предках цели, определенных до отправки.
Некоторые события могут не обязательно выполнять три фазы потока событий DOM, например, событие может быть определено только для одной или двух фаз. В качестве примера, события, определенные в этой спецификации, всегда будут выполнять фазы захвата и назначения, но некоторые не будут завершать фазу пузырьков ("события пузырьков" по сравнению с "событиями не пузырьков", см. Также атрибут Event.bubbles).
Событие захвата против события пузыря
- Событие захвата будет отправлено до Bubble Event.
- Порядок распространения событий
- Родительский захват
- Захват детей
- Детский пузырь
- Родительский пузырь
(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. Распределение событий (также называемое распространением событий) происходит в три этапа и в следующем порядке:
- Фаза захвата: событие отправляется предкам цели из корня дерева (
document
) к прямому родителю целевого узла. - Целевая фаза: событие отправляется на целевой узел. Целевая фаза всегда на самом глубоком
html
элемент, на котором было отправлено событие. - Фаза пузырьков: событие отправляется предкам цели от прямого родителя целевого узла к корню дерева.
Пример:
// 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
Учитывая три этапа событийного путешествия:
- Фаза захвата: событие отправляется предкам цели от корня дерева до прямого родителя целевого узла.
- Целевая фаза: событие отправляется на целевой узел.
- Фаза пузырьков: событие отправляется предкам цели от прямого родителя целевого узла к корню дерева.
useCapture
указывает, для каких фаз будет проходить событие:
Если
true
, useCapture указывает, что пользователь желает добавить прослушиватель событий только для фазы захвата, то есть этот прослушиватель событий не будет запущен во время целевых и пузырьковых фаз. Еслиfalse
прослушиватель событий будет срабатывать только во время целевого и пузырькового этапов
Источник совпадает со вторым лучшим ответом: https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html
Порядок определения имеет значение, только если элементы находятся на одном уровне. Если вы измените порядок определения в вашем коде, вы получите те же результаты.
Однако, если вы измените настройку useCapture в двух обработчиках событий, дочерний обработчик отклика будет предшествовать родительскому обработчику. Причина этого заключается в том, что дочерний обработчик событий теперь будет запускаться на фазе захвата, которая предшествует фазе пузырьков, на которой будет запускаться родительский обработчик событий.
Если вы установите для свойства useCapture значение true для обоих обработчиков событий - независимо от порядка определения - родительский обработчик событий будет запущен первым, потому что он предшествует дочернему на этапе захвата.
И наоборот, если вы установите для свойства useCapture значение false для обоих обработчиков событий - опять же, независимо от порядка определения - дочерний обработчик событий будет запущен первым, потому что он предшествует родительскому в фазе пузырьков.