无法理解addEventListener中的useCapture参数


290

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

如果为true,则useCapture指示用户希望启动捕获。启动捕获后,所有指定类型的事件都将分派给注册的侦听器,然后才分派给DOM树中其下的任何EventTarget。在树中冒泡的事件不会触发指定使用捕获的侦听器。

在此代码中,parent事件在child之前触发,因此我无法理解其行为。Document对象的usecapture为true,child div的usecapture设置为false,并且遵循了useuseture的文档。因此为什么document属性优先于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>

Answers:


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事件:“实现必须确定当前目标的候选事件监听器这必须是已经被登记在当前目标的所有事件侦听器的列表他们。注册顺序。”
罗布W

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
目标节点的子节点怎么样?他们何时获得活动?
Aurimas

树的根实际上是因为是的子Window而是根?documentdocumentWindow
stackjlei

此图很有帮助。谢谢 !
Thomas An

1
我只希望所有解释“什么”的资源都包括“为什么”。像往常一样使用更多谷歌搜索。
aaaaaa

80

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

MDN参考

  • 捕获事件将在泡泡事件之前分派
  • 事件传播顺序为
    1. 家长捕捉
    2. 儿童捕捉
    3. 目标捕获和目标泡沫
      • 按照注册顺序
      • 当元素是事件的目标时,useCapture参数无关紧要(感谢@bam和@ legend80s)
    4. 儿童泡泡
    5. 父母泡泡
  • stopPropagation() 将停止流动

使用捕获流

演示版

结果:

  1. 家长捕捉
  2. 目标泡泡1

    (因为Capture和Target的Bubble将按照注册顺序触发,因此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.子气泡这很重要!仅因为如果Child将成为事件的目标,则将以相同的顺序调用侦听器。请参阅MDN上的注释:当元素是事件“ useCapture”参数的目标时,不要紧。(developer.mozilla.org/en-US/docs/Web/API/EventTarget/...
BAM

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”,而该图表明“捕获”对于任何元素都应始终优先出现!
Gershom

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

这里的脚本是从内部元素执行的:事件冒泡(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

这里的脚本是从祖先/外部元素执行的:事件捕获(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

这里的脚本是从内部元素执行的:事件冒泡(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

这里的脚本是从祖先/外部元素执行的:由于useCapture设置为true,因此进行事件捕获


1
在这种情况下,“大于” V形的含义是什么?
2540625 '18

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


2

仅当项目处于相同级别时,定义顺序才重要。如果您颠倒了代码中的定义顺序,您将获得相同的结果。

但是,如果您反转两个事件处理程序上的useCapture设置,则子事件处理程序将在父事件处理程序之前响应。这样做的原因是,现在将在捕获阶段之前触发子事件处理程序,而捕获阶段将在触发父事件处理程序的冒泡阶段之前进行。

如果将两个事件处理程序的useCapture都设置为true(无论定义顺序如何),则将首先触发父事件处理程序,因为它在捕获阶段位于子项之前。

相反,如果将两个事件处理程序的useCapture都设置为false(同样与定义顺序无关),则子事件处理程序将首先被触发,因为它在冒泡阶段位于父项之前。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.