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


描述:

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>


冒泡

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

捕捉

  Event propagate from body(root) element to eventTriggered Element is **CAPTURING**.

正如其他人所说,冒泡和捕获描述了一些嵌套元素接收给定事件的顺序。

我想指出,对于最里面的元素可能会出现一些奇怪的东西。实际上,在这种情况下,添加事件侦听器的顺序确实很重要。

在下面的例子中,捕获div2将先执行,然后再执行冒泡;当为div4冒泡时,将先执行而不是捕获。

function addClickListener (msg, num, type) { document.querySelector("#div" + num) .addEventListener("click", () => alert(msg + num), type); } bubble = (num) => addClickListener("bubble ", num, false); capture = (num) => addClickListener("capture ", num, true); // first capture then bubble capture(1); capture(2); bubble(2); bubble(1); // try reverse order bubble(3); bubble(4); capture(4); capture(3); #div1, #div2, #div3, #div4 { border: solid 1px; padding: 3px; margin: 3px; } <div id="div1"> div 1 <div id="div2"> div 2 </div> </div> <div id="div3"> div 3 <div id="div4"> div 4 </div> </div>

编辑:这种行为可能因浏览器而异(例如,目前在Firefox上出现,但在Chrome和Edge上没有)。然而,我认为人们应该意识到这一点。


DOM Events描述了事件传播的3个阶段:捕获阶段——事件向下到元素。目标阶段——事件到达目标元素。冒泡阶段——事件从元素冒泡。


当浏览器检测到事件时,它会尝试查找事件处理程序。这个过程有三个阶段。假设我们有这些元素

   <body>
      <div>
        <button>click</button>
      </div>
    </body>

1-Capture阶段

browser is going to take a look at the element that was just clicked. Then it will go to the top parent element which is body. if there is any click handle in body, the browser will call it. after checking the body element, it will look at the second top parent element which is div element. This process will repeat until the browser gets down to the button element at the very bottom. Once it sees the button element, Phase One- Capturing will be over. Most of the time we ignore this phase. This code ignores this phase

document.addEventListener('click',handleClick)

如果你想涉及到这一阶段,你必须这样写

document.addEventListener('click',handleClick,true)

当我们需要检测目标之外的点击时,我们就需要这个阶段。也许我们有一个打开的下拉菜单或者模态菜单如果用户点击了模态菜单或者下拉菜单之外的任何地方,我们想要关闭它

2-Target阶段

浏览器会查看被点击的按钮元素,如果按钮有事件处理程序,浏览器会调用它。

3-气泡阶段

与捕获阶段相反,在这个阶段,浏览器将从直接的父元素(在这种情况下是div元素)开始这个过程,然后它将访问body元素。这段代码将为气泡阶段设置事件处理程序

document.addEventListener('click',handleClick,false)