it-swarm.cn

无法理解的的addEventListener中的将将useCapture参数

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

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

在这段代码中,父事件在子事件之前触发,因此我无法理解其行为。文档对象具有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>
265
user26732

事件可以在两种情况下激活:开始(“捕获”)和结束(“泡沫”)。事件按照它们的定义顺序执行。比如说,你定义了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定义的事件)
325
Rob W

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

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

阶段

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

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

graphical representation of an event dispatched in a DOM tree using the DOM event flow

目标的祖先在事件的初始发送之前确定。如果在调度期间删除了目标节点,或者添加或删除了目标的祖先,则事件传播将始终基于目标节点和在调度之前确定的目标的祖先。

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

244
lax4mike

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

MDN参考

  • Capture Event将在Bubble Event之前发送
  • 事件传播顺序为
    1. 父捕获
    2. 儿童捕获
    3. 目标捕获和目标泡沫
      • 按照他们的注册顺序
      • 当元素是事件的目标时,useCapture参数无关紧要(谢谢@bam和@ legend80s)
    4. 儿童泡泡
    5. 父泡泡

stopPropagation()将停止流动

use Capture flow

演示

结果:

  1. 父捕获
  2. 儿童泡泡1
  3. 儿童捕获

    (因为儿童是目标,因此捕获和泡泡将按照他们注册的顺序触发)

  4. 儿童泡泡2
  5. 父泡泡
var parent = document.getElementById('parent'),
    children = document.getElementById('children');

children.addEventListener('click', function (e) { 
    alert('Children Bubble 1');
    // e.stopPropagation();
}, false);

children.addEventListener('click', function (e) { 
    alert('Children Capture');
    // e.stopPropagation();
}, true);

children.addEventListener('click', function (e) { 
    alert('Children Bubble 2');
    // e.stopPropagation();
}, false);

parent.addEventListener('click', function (e) { 
    alert('Parent Capture');
    // e.stopPropagation();
}, true);

parent.addEventListener('click', function (e) { 
    alert('Parent Bubble');
    // e.stopPropagation();
}, false);
<div id="parent">
    <div id="children">
        Click
    </div>
</div>
66
Steely Wing

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

14
sushil bharwani

代码示例:

<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

这里的脚本是从内部元素执行的: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);

执行:点击内部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

这里的脚本是从内部元素执行的: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);

执行:点击内部Div,警报显示为:Div 1> Div 2

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

11
shadowBot

这些都与事件模型有关: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow 您可以在冒泡阶段或捕获阶段捕获事件。你的选择。
看看 http://www.quirksmode.org/js/events_order.html - 你会发现它非常有用。

10
NilColor

鉴于事件旅行的三个阶段:

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

useCapture表示事件travel将在哪些阶段:

如果true useCapture 表示用户希望仅为捕获阶段添加事件侦听器,即在目标和冒泡阶段不会触发此事件侦听器。如果false,则仅在目标和冒泡阶段触发事件侦听器

来源与第二个最佳答案相同: https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

6
Aurimas

摘要:

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. 冒泡阶段: 将事件从目标节点的直接父节点分派到目标的祖先到树的根节点。

Event bubbling, event capturing, event target

例:

// 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),然后通过事件冒泡触发父元素中的事件(记录outerBubble)。

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

5
Willem van der Veen

只有当项目处于同一级别时,定义顺序才有意义。如果您颠倒代码中的定义顺序,您将得到相同的结果。

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

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

相反,如果为两个事件处理程序将useCapture设置为false - 再次无论定义顺序如何 - 将首先触发子事件处理程序,因为它在冒泡阶段的父级之前。

1
WXB13