无法理解 addEventListener 中的 useCapture 参数

IT技术 javascript dom dom-events
2021-02-11 04:17:11

我已阅读https://developer.mozilla.org/en/DOM/element.addEventListener 上的文章,但无法理解useCapture属性。定义有:

如果为 true,则 useCapture 表示用户希望启动捕获。启动捕获后,指定类型的所有事件将被分派到注册的侦听器,然后再分派到 DOM 树中它下面的任何 EventTarget。通过树向上冒泡的事件不会触发指定使用捕获的侦听器。

在这段代码中,父事件在子事件之前触发,所以我无法理解它的行为。文档对象的 usecapture 为 true,子 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>

6个回答

事件可以在两种情况下激活:开始时(“捕获”)和结束时(“泡沫”)。事件按照定义的顺序执行。假设您定义了 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
@slier,是的,同一事件的多个处理程序执行的顺序。
2021-03-19 04:17:11
所以这基本上与我猜的事件顺序有关
2021-03-24 04:17:11
@tjameson执行的顺序在继承了DOM2规范保证,DOM3事件:“实现必须确定当前目标的候选事件监听器这必须是已经被登记在当前目标的所有事件侦听器的列表他们。登记顺序。”
2021-03-27 04:17:11
不知道为什么这是公认的答案,因为 afaik,捕获和冒泡谈论传播行为而不是规定多个相邻事件处理程序的执行顺序
2021-04-08 04:17:11
不保证执行顺序no specification is made as to the order in which they will receive the event with regards to the other EventListeners on the EventTarget我还没有测试过所有的浏览器,所以它们可能只是碰巧以相同的方式实现它。然而,捕获事件将在非捕获事件之前完成。
2021-04-13 04:17:11

我发现这个图对于理解捕获/目标/气泡阶段非常有用:http : //www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

下面,从链接中提取的内容。

阶段

该事件按照从树的根到此目标节点的路径进行调度。然后可以在目标节点级别或从树中更高的任何目标的祖先进行本地处理。事件分派(也称为事件传播)分三个阶段和以下顺序发生:

  1. 捕获阶段:从树的根到目标节点的直接父节点,将事件分派给目标的祖先。
  2. 目标阶段:将事件分派到目标节点。
  3. 冒泡阶段:从目标节点的直接父节点到树的根节点,将事件分派给目标的祖先。

使用 DOM 事件流在 DOM 树中调度的事件的图形表示

目标的祖先是在事件的初始分派之前确定的。如果在调度过程中目标节点被移除,或者目标的祖先被添加或移除,则事件传播将始终基于目标节点和在调度之前确定的目标的祖先。

有些事件不一定完成DOM 事件流的三个阶段,例如,事件只能定义为一个或两个阶段。例如,本规范中定义的事件将始终完成捕获和目标阶段,但有些不会完成冒泡阶段(“冒泡事件”与“非冒泡事件”,另见 Event.bubbles 属性)。

目标节点的孩子怎么样?他们什么时候收到事件?
2021-03-16 04:17:11
我只是希望所有解释“什么”的资源都包含“为什么”。像往常一样进行更多的谷歌搜索。
2021-03-22 04:17:11
@Aurimas 他们没有,这没有意义。目标是应该接收事件的最里面的元素。如果你点击 <body> 元素(一个空的地方),<body> 内的所有元素(= 页面的所有元素)显然不应该收到点击事件。
2021-03-28 04:17:11
很不错的图!
2021-04-04 04:17:11
树的根实际上是Window不是document,因为document是 的孩子Window
2021-04-09 04:17:11

捕获事件 ( useCapture = true) 与气泡事件 ( useCapture = false)

MDN 参考

  • 捕获事件将在气泡事件之前调度
  • 事件传播顺序是
    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>

示例中有一个错误:您按以下顺序声明了子事件: 1. child capture 2. child bubble 很重要!仅仅因为如果 Child 将成为事件的目标,则将按相同顺序调用侦听器。请参阅 MDN 上的注释:当元素是事件 'useCapture' 参数的目标时并不重要。developer.mozilla.org/en-US/docs/Web/API/EventTarget/...
2021-03-28 04:17:11
这解释了为什么运行该示例会在“儿童捕获”之前产生“儿童气泡 1”,此时图表表明对于任何元素都应始终首先发生“捕获”!
2021-04-08 04:17:11
注意:对于附加到事件目标的事件侦听器,事件处于目标阶段,而不是捕获和冒泡阶段。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/...所以不存在“儿童捕获”和“儿童泡泡”阶段。
2021-04-11 04:17:11

当您说 useCapture = true 时,事件在捕获阶段从上到下执行,当为 false 时,它​​会从下到上进行气泡。

概括:

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为时,此 a允许事件侦听器使用事件捕获而不是事件冒泡。

在我们的示例中,当我们将 useCapture 参数设置为时,false我们会看到事件冒泡发生。首先触发目标阶段的事件(记录innerBubble),然后通过事件冒泡触发父元素中的事件(记录outerBubble)。

当我们将 useCapture 参数设置为 时,true我们会看到外部事件<div>首先被触发。这是因为事件现在在捕获阶段而不是冒泡阶段被触发。