Неможливо зрозуміти параметр useCapture в addEventListener


290

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

Якщо це правда, UseCapture вказує на те, що користувач бажає ініціювати захоплення. Після ініціювання захоплення всі події зазначеного типу будуть відправлені зареєстрованому слухачеві, перш ніж надсилатись до будь-яких подій EventTargets під ним у дереві DOM. Події, що киплять вгору через дерево, не спричинять слухача, призначеного використовувати захоплення.

У цьому коді батьківський подія спрацьовує перед дитиною, тому я не в змозі зрозуміти його поведінку. Об'єкт "Документ" має істинний usecapture, і дочірній div має набір usecapture false, а документ usecapture дотримується. Тому чому властивість документа віддається перевазі над дочірнім.

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>

Відповіді:


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)

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


1
дуже приємна схема!
Олексій

1
Як щодо дітей цільового вузла? Коли вони отримують подію?
Аурімас

Чи справді Windowзамість цього корінь дерева document, тому що documentце дитина Window?
stackjlei

1
@Aurimas їх немає, це не мало б сенсу. Ціль - це найбільший внутрішній елемент, який повинен приймати подію. Якщо ви натискаєте елемент <body> (порожнє місце), всі елементи всередині <body> (= всі елементи сторінки), очевидно, не повинні отримувати події клацання.
amik

1
Я просто бажаю, щоб усі ресурси, які пояснювали "що" включали "чому". Вимкнено для більш гуглих, як зазвичай.
aaaaaa

80

Захоплення події ( useCapture = true) проти Bubble Event ( useCapture = false)

Довідка про MDN

  • Захід Capture буде відправлений перед подією Bubble
  • Порядок розповсюдження події є
    1. Захоплення батьків
    2. Захоплення дітей
    3. Цільовий захоплення та цільовий міхур
      • У порядку їх реєстрації
      • Коли елемент є ціллю події, useCaptureпараметр не має значення (Спасибі @bam та @ legend80s)
    4. Діти міхур
    5. Батьківський міхур
  • stopPropagation() зупинить потік

використовувати Capture flow

Демо

Результат:

  1. Захоплення батьків
  2. Цільовий міхур 1

    (Тому що Capture and 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>


1
Прикладом є помилка: ви оголосили дитячі події у порядку: 1. захоплення дитини 2. міхур дитини Це має значення! Просто тому, що якщо Дитина стане ціллю події, слухачів буде викликано в тому ж порядку. Дивіться примітку на 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/… . Тож не існує жодної фази «Захоплення дітей» та «Діти міхур».
legend80s

І це пояснює, чому запуск прикладу створює "Діти міхур 1" перед "Захоплення дітей", коли діаграма передбачає, що "захоплення" завжди має відбуватися спочатку для будь-якого елемента!
Гершом

18

Коли ви говорите useCapture = true, у фазі захоплення події виконують зверху вниз, коли false - це бульбашка знизу вгору.


11

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


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

Виконує: натискання Внутрішньої Div, сповіщення відображаються у вигляді: Div 2> Div 1

Тут сценарій виконується з внутрішнього елемента: 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);

Виконує: натискання Внутрішньої Div, сповіщення відображаються у вигляді: Div 1> Div 2

Тут сценарій виконується з предка / зовнішнього елемента: 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);

Виконує: натискання Внутрішньої Div, сповіщення відображаються у вигляді: Div 2> Div 1

Тут сценарій виконується з внутрішнього елемента: 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);

Виконує: натискання Внутрішньої Div, сповіщення відображаються у вигляді: Div 1> Div 2

Тут сценарій виконується з предка / зовнішнього елемента: Event Capturing з моменту useCapture встановлено на true


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ми бачимо, що відбувається міхур події. Спочатку подія на цільовій фазі запускається (журнали InternalBubble), а потім через бульбашку події запускається подія в батьківському елементі (logs 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


2

Порядок визначення має значення лише в тому випадку, якщо елементи знаходяться на одному рівні. Якщо ви скасуєте порядок визначення у своєму коді, ви отримаєте однакові результати.

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

Якщо встановити функцію useCapture істинною для обох обробників подій - незалежно від порядку визначення - батьківський обробник подій буде запущений спочатку, оскільки він переходить до дитини у фазі захоплення.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.