事件冒泡和捕获之间的区别是什么?什么时候应该使用冒泡和捕获?


当前回答

事件冒泡和捕获是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元素处理。

有关更多信息,请参见

QuirksMode上的事件顺序 addEventListener在MDN QuirksMode上的高级事件

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

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):

Event capturing When you use event capturing | | ---------------| |----------------- | element1 | | | | -----------| |----------- | | |element2 \ / | | | ------------------------- | | Event CAPTURING | ----------------------------------- the event handler of element1 fires first, the event handler of element2 fires last. Event bubbling When you use event bubbling / \ ---------------| |----------------- | element1 | | | | -----------| |----------- | | |element2 | | | | | ------------------------- | | Event BUBBLING | ----------------------------------- the event handler of element2 fires first, the event handler of element1 fires last.


用什么?

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

事件冒泡和捕获是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元素处理。

有关更多信息,请参见

QuirksMode上的事件顺序 addEventListener在MDN QuirksMode上的高级事件

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

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的另一个例子。

If there are two elements element 1 and element 2. Element 2 is inside element 1 and we attach an event handler with both the elements lets say onClick. Now when we click on element 2 then eventHandler for both the elements will be executed. Now here the question is in which order the event will execute. If the event attached with element 1 executes first it is called event capturing and if the event attached with element 2 executes first this is called event bubbling. As per W3C the event will start in the capturing phase until it reaches the target comes back to the element and then it starts bubbling

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

eventTarget.addEventListener(类型、侦听器(useCapture]);

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

var div1 = document.querySelector("#div1"); var div2 = document.querySelector("#div2"); div1。addEventListener(“点击”,函数(事件){ 警报(“您点击了div 1”); },真正的); div2。addEventListener(“点击”,函数(事件){ 警报(“您点击了div 2”); },假); # div1 { 背景颜色:红色; 填充:24 px; } # div2 { 背景颜色:绿色; } < div id = " div1 " > div 1 < div id = " div2 " > div 2 < / div > < / div >

请试着改变真假。

我发现这个教程在javascript.info中非常清楚地解释了这个主题。它最后的3点总结是对关键点的讨论。我在这里引用:

事件首先被捕捉到最深的目标,然后向上冒泡。在 IE<9,它们只是冒泡。 所有处理程序都在冒泡阶段上工作,除了 addEventListener的最后一个参数为真,这是唯一的方法 在捕获阶段捕获事件。 冒泡/捕获可以 由事件停止。cancelBubble=true (IE)或event.stopPropagation() 对于其他浏览器。

还有一个事件。eventPhase属性,它可以告诉你事件是在目标还是来自其他地方,浏览器完全支持。

从已接受的答案扩展已经很好的代码片段,这是使用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>