无法理解addEventListener中的useCapture参数

时间:2011-09-13 07:29:44

标签: javascript dom javascript-events dom-events

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

  

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

在此代码中,父事件在子事件之前触发,因此我无法理解它 behavior.Document对象有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 个答案:

答案 0 :(得分:327)

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

答案 1 :(得分:248)

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

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

<强>阶段

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

  1. 捕获阶段:将事件分派给目标的祖先 从树的根到目标节点的直接父节点。
  2. 目标阶段:将事件分派到目标节点。
  3. 冒泡阶段:事件被分派到目标 祖先从目标节点的直接父节点到根节点 树。
  4. graphical representation of an event dispatched in a DOM tree using the DOM event flow

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

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

答案 2 :(得分:69)

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

MDN Reference

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

stopPropagation()将停止流程

use Capture flow

演示

结果:

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

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

  4. 儿童泡泡2
  5. 父母泡泡
  6. 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>

答案 3 :(得分:14)

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

答案 4 :(得分: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

答案 5 :(得分:10)

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

答案 6 :(得分:6)

鉴于事件旅行的三个阶段

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

useCapture表示旅行的活动将在哪个阶段:

  

如果true useCapture 表示用户希望添加活动   仅捕获阶段的侦听器,即此事件侦听器不会   在目标和冒泡阶段被触发。如果是false,那么   事件侦听器只会在目标和冒泡期间触发   相

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

答案 7 :(得分:5)

摘要:

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

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

答案 8 :(得分:1)

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

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

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

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