什么是事件冒泡和捕获?


Answers:


1438

事件冒泡和捕获是HTML DOM API中事件传播的两种方式,当一个事件发生在另一个元素内的一个元素中,并且两个元素都注册了该事件的句柄时。事件传播模式确定元素什么顺序接收事件

冒泡时,事件首先由最里面的元素捕获和处理,然后传播到外面的元素。

通过捕获,事件首先被最外面的元素捕获并传播到内部元素。

捕获也称为“滴答”,它有助于记住传播顺序:

down流,冒泡

过去,Netscape提倡事件捕获,而Microsoft提倡事件冒泡。两者都是W3C 文档对象模型事件标准(2000)的一部分。

IE <9 使用事件冒泡,而IE9 +和所有主流浏览器均支持。另一方面,对于复杂的DOM ,事件冒泡性能可能会稍低

我们可以addEventListener(type, listener, useCapture)在冒泡(默认)或捕获模式下使用来注册事件处理程序。要使用捕获模型,请将第三个参数传递为true

<div>
    <ul>
        <li></li>
    </ul>
</div>

在上面的结构中,假定li元素中发生了单击事件。

在捕获模型中,事件将首先处理(将div首先div触发will 中的click事件处理程序),然后在中处理ul,然后在目标元素中最后处理li

在冒泡模型中,将发生相反的情况:事件将首先由li,然后由ul,最后由div元素处理。

有关更多信息,请参见

在下面的示例中,如果单击任何突出显示的元素,则可以看到事件传播流的捕获阶段首先发生,然后是冒泡阶段。

var logElement = document.getElementById('log');

function log(msg) {
    logElement.innerHTML += ('<p>' + msg + '</p>');
}

function capture() {
    log('capture: ' + this.firstChild.nodeValue.trim());
}

function bubble() {
    log('bubble: ' + this.firstChild.nodeValue.trim());
}

function clearOutput() {
    logElement.innerHTML = "";
}

var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
    divs[i].addEventListener('click', capture, true);
    divs[i].addEventListener('click', bubble, false);
}
var clearButton = document.getElementById('clear');
clearButton.addEventListener('click', clearOutput);
p {
    line-height: 0;
}

div {
    display:inline-block;
    padding: 5px;

    background: #fff;
    border: 1px solid #aaa;
    cursor: pointer;
}

div:hover {
    border: 1px solid #faa;
    background: #fdd;
}
<div>1
    <div>2
        <div>3
            <div>4
                <div>5</div>
            </div>
        </div>
    </div>
</div>
<button id="clear">clear output</button>
<section id="log"></section>

JSFiddle的另一个示例


41
useCapture现在在IE> = 9中受支持。来源
beatgammit 2013年

7
我知道为时已晚,不能发表评论,但是我在这里找到了不错的文章catcode.com/domcontent/events/capture.html
刚刚

3
triclkling一样的capturing吗?关于克罗克福德会谈Trickling v. Bubbling在此视频通话- youtube.com/watch?v=Fv9qT9joc0M&list=PL7664379246A246CB左右1 hr 5 minutes
凯文·梅雷迪斯

1
@KevinMeredith同样的事情。“点滴”只是使您更容易记住两个模型的作用(向下滴流,向上泡)。
一只猫

7
上面的答案在详细的说明中就顺序而言是正确的,但您会以为“ trick流,up流”继而发生second流。事件总是在气泡阶段之前经过捕获阶段。正确的顺序是trickle down=> onElement=>bubble up
2015年

513

描述:

quirksmode.org对此有很好的描述。简而言之(从quirksmode复制):

事件捕捉

使用事件捕获时

               | |
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  \ /          |     |
|   -------------------------     |
|        Event CAPTURING          |
-----------------------------------

element1的事件处理程序首先触发,element2的事件处理程序最后触发。

事件冒泡

使用事件冒泡时

               / \
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  | |          |     |
|   -------------------------     |
|        Event BUBBLING           |
-----------------------------------

element2的事件处理程序首先触发,element1的事件处理程序最后触发。


使用什么?

这取决于您想做什么。没有比这更好的了。区别在于事件处理程序的执行顺序。在大多数情况下,在冒泡阶段触发事件处理程序是可以的,但也有必要更早地触发它们。


第一次捕获和冒泡都不会发生,什么是调度事件?
Suraj Jain

一个图形示例在这里:javascript.info/bubbling-and-capturing
社区Ans

71

如果元素1和元素2有两个元素,则元素2在元素1内,我们将事件处理程序与这两个元素相连,可以说onClick。现在,当我们单击元素2时,两个元素的eventHandler将被执行。现在,这里的问题是事件将按什么顺序执行。如果首先执行元素1附加的事件,则称为事件捕获;如果首先执行元素2附加的事件,则称为事件冒泡。根据W3C,事件将在捕获阶段开始,直到到达目标并返回到元素,然后开始冒泡

通过addEventListener方法的useCapture参数可以知道捕获和冒泡状态

eventTarget.addEventListener(type,listener,[,useCapture]);

默认情况下,useCapture为false。这意味着它处于冒泡阶段。

var div1 = document.querySelector("#div1");
var div2 = document.querySelector("#div2");

div1.addEventListener("click", function (event) {
  alert("you clicked on div 1");
}, true);

div2.addEventListener("click", function (event) {
  alert("you clicked on div 2");
}, false);
#div1{
  background-color:red;
  padding: 24px;
}

#div2{
  background-color:green;
}
<div id="div1">
  div 1
  <div id="div2">
    div 2
  </div>
</div>

请尝试更改是非。


2
@masterxilo:不需要Fiddle,StackOverflow现在支持内联代码(堆栈片段)
Dan Dascalescu 2014年

关于the event will start in the capturing phase untill it reaches the target comes back to the element and then it starts bubbling。我只发现 addEventListener具有useCapture可以设置为true或false 的参数;和在HTML 4.0,事件侦听器被指定为元素的属性useCapture defaults to false。您能否链接到可以确认您所写内容的规范?
surfmuggle's

25

在javascript.info上发现了本教程,在解释该主题时非常清楚。最后,它的三点总结确实在说明关键点。我在这里引用:

  1. 首先将事件捕获到最深的目标,然后再冒泡。在IE <9中,它们只会冒泡。
  2. addEventListener最后一个参数外true,所有处理程序都在冒泡阶段工作 ,这是在捕获阶段捕获事件的唯一方法。
  3. 冒泡/捕获可以通过event.cancelBubble=true(IE)或event.stopPropagation() 其他浏览器来停止。

7

还有一个Event.eventPhase属性可以告诉您事件是目标事件还是其他事件。

请注意,尚未确定浏览器的兼容性。我已经在Chrome(66.0.3359.181)和Firefox(59.0.3)上对其进行了测试,并且该工具在其中受支持。

从接受的答案扩展本来就很不错的代码段,这是使用eventPhase属性的输出

var logElement = document.getElementById('log');

function log(msg) {
  if (logElement.innerHTML == "<p>No logs</p>")
    logElement.innerHTML = "";
  logElement.innerHTML += ('<p>' + msg + '</p>');
}

function humanizeEvent(eventPhase){
  switch(eventPhase){
    case 1: //Event.CAPTURING_PHASE
      return "Event is being propagated through the target's ancestor objects";
    case 2: //Event.AT_TARGET
      return "The event has arrived at the event's target";
    case 3: //Event.BUBBLING_PHASE
      return "The event is propagating back up through the target's ancestors in reverse order";
  }
}
function capture(e) {
  log('capture: ' + this.firstChild.nodeValue.trim() + "; " + 
  humanizeEvent(e.eventPhase));
}

function bubble(e) {
  log('bubble: ' + this.firstChild.nodeValue.trim() + "; " + 
  humanizeEvent(e.eventPhase));
}

var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
  divs[i].addEventListener('click', capture, true);
  divs[i].addEventListener('click', bubble, false);
}
p {
  line-height: 0;
}

div {
  display:inline-block;
  padding: 5px;

  background: #fff;
  border: 1px solid #aaa;
  cursor: pointer;
}

div:hover {
  border: 1px solid #faa;
  background: #fdd;
}
<div>1
  <div>2
    <div>3
      <div>4
        <div>5</div>
      </div>
    </div>
  </div>
</div>
<button onclick="document.getElementById('log').innerHTML = '<p>No logs</p>';">Clear logs</button>
<section id="log"></section>


5

起泡

  Event propagate to the upto root element is **BUBBLING**.

捕捉

  Event propagate from body(root) element to eventTriggered Element is **CAPTURING**.
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.