事件冒泡和捕获之间的区别是什么?什么时候应该使用冒泡和捕获?
当前回答
我发现这个教程在javascript.info中非常清楚地解释了这个主题。它最后的3点总结是对关键点的讨论。我在这里引用:
事件首先被捕捉到最深的目标,然后向上冒泡。在 IE<9,它们只是冒泡。 所有处理程序都在冒泡阶段上工作,除了 addEventListener的最后一个参数为真,这是唯一的方法 在捕获阶段捕获事件。 冒泡/捕获可以 由事件停止。cancelBubble=true (IE)或event.stopPropagation() 对于其他浏览器。
其他回答
我发现这个教程在javascript.info中非常清楚地解释了这个主题。它最后的3点总结是对关键点的讨论。我在这里引用:
事件首先被捕捉到最深的目标,然后向上冒泡。在 IE<9,它们只是冒泡。 所有处理程序都在冒泡阶段上工作,除了 addEventListener的最后一个参数为真,这是唯一的方法 在捕获阶段捕获事件。 冒泡/捕获可以 由事件停止。cancelBubble=true (IE)或event.stopPropagation() 对于其他浏览器。
描述:
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.
用什么?
这取决于你想做什么。没有更好的了。不同之处在于事件处理程序的执行顺序。大多数情况下,在冒泡阶段触发事件处理程序是可以的,但也有必要在更早的时候触发它们。
还有一个事件。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>
事件冒泡和捕获是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的另一个例子。
正如其他人所说,冒泡和捕获描述了一些嵌套元素接收给定事件的顺序。
我想指出,对于最里面的元素可能会出现一些奇怪的东西。实际上,在这种情况下,添加事件侦听器的顺序确实很重要。
在下面的例子中,捕获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上没有)。然而,我认为人们应该意识到这一点。
推荐文章
- 给一个数字加上st, nd, rd和th(序数)后缀
- 如何以编程方式触发引导模式?
- setTimeout带引号和不带括号的区别
- 在JS的Chrome CPU配置文件中,'self'和'total'之间的差异
- 用javascript检查输入字符串中是否包含数字
- 如何使用JavaScript分割逗号分隔字符串?
- 在Javascript中~~(“双波浪号”)做什么?
- 谷歌chrome扩展::console.log()从后台页面?
- 未捕获的SyntaxError:
- [].slice的解释。调用javascript?
- jQuery日期/时间选择器
- 我如何预填充一个jQuery Datepicker文本框与今天的日期?
- 数组的indexOf函数和findIndex函数的区别
- jQuery添加必要的输入字段
- Access-Control-Allow-Origin不允许Origin < Origin >