协慌网

登录 贡献 社区

什么是事件冒泡和捕获?

事件冒泡和捕获之间有什么区别?在这两个中,哪个是更快更好的模型?

答案

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

通过冒泡,事件首先被最内层元素捕获并处理,然后传播到外部元素。

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

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

涓涓细流,泡沫起来

在过去,Netscape 主张事件捕获,而微软则推动事件冒泡。两者都是 W3C 文档对象模型事件标准(2000)的一部分。

IE <9 使用事件冒泡 ,而 IE9 + 和所有主要浏览器都支持这两种情况。另一方面,对于复杂的 DOM ,事件冒泡性能可能略低

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

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

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

在拍摄模式下,事件将被处理div第一(单击事件处理程序div将触发第一个),然后在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 的另一个例子

描述:

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

事件捕获

使用事件捕获时

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

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

事件冒泡

当您使用事件冒泡时

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

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


用什么?

这取决于你想做什么。没有更好的。不同之处在于事件处理程序的执行顺序。大多数情况下,在冒泡阶段发射事件处理程序是可以的,但也可能需要提前解雇它们。

如果有两个元素元素 1 和元素 2. 元素 2 在元素 1 内部,我们附加一个事件处理程序,两个元素让我们说 onClick。现在当我们点击元素 2 时,将执行两个元素的 eventHandler。现在问题在于事件将以何种顺序执行。如果首先执行附加了元素 1 的事件,则称为事件捕获,如果首先执行附加元素 2 的事件,则称为事件冒泡。根据 W3C,事件将在捕获阶段开始,直到它到达目标回到元素然后它开始冒泡

捕获和冒泡状态由 addEventListener 方法的 useCapture 参数已知

eventTarget.addEventListener(类型,听众,[方法,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>

请尝试改变真假。