事件冒泡和捕获
针对 DOM 元素触发的事件不仅会影响它们所定位的元素。DOM 中任何目标的祖先也可能有机会对事件作出反应。请考虑以下文档:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<p id="paragraph">
<span id="text">Hello World</span>
</p>
</body>
</html>
如果我们只是在没有任何选项的情况下为每个元素添加侦听器,那么触发对 span 的单击…
document.body.addEventListener('click', function(event) {
console.log("Body clicked!");
});
window.paragraph.addEventListener('click', function(event) {
console.log("Paragraph clicked!");
});
window.text.addEventListener('click', function(event) {
console.log("Text clicked!");
});
window.text.click();
…然后事件将通过每个祖先冒泡,在途中触发每个点击处理程序:
Text clicked!
Paragraph clicked!
Body clicked!
如果你希望其中一个处理程序阻止事件触发更多处理程序,则可以调用 event.stopPropagation()
方法。例如,如果我们用这个替换第二个事件处理程序:
window.paragraph.addEventListener('click', function(event) {
console.log("Paragraph clicked, and that's it!");
event.stopPropagation();
});
我们会看到以下输出,body
的 click
处理程序从未触发:
Text clicked!
Paragraph clicked, and that's it!
最后,我们可以选择添加在捕获 期间触发而不是冒泡的事件侦听器。在事件从元素通过其祖先冒泡之前,它首先通过其祖先捕获到元素。通过将 true
或 {capture: true}
指定为 addEventListener
的可选第三个参数来添加捕获侦听器。如果我们在上面的第一个例子中添加以下监听器:
document.body.addEventListener('click', function(event) {
console.log("Body click captured!");
}, true);
window.paragraph.addEventListener('click', function(event) {
console.log("Paragraph click captured!");
}, true);
window.text.addEventListener('click', function(event) {
console.log("Text click captured!");
}, true);
我们将得到以下输出:
Body click captured!
Paragraph click captured!
Text click captured!
Text clicked!
Paragraph clicked!
Body clicked!
默认情况下,在冒泡阶段会侦听事件。要更改此设置,你可以通过在 addEventListener 函数中指定第三个参数来指定事件被侦听的阶段。 (要了解捕获和冒泡,请查看备注 )
element.addEventListener(eventName, eventHandler, useCapture)
useCapture:true
表示当它沿着 DOM 树向下时监听事件。false
意味着在它上升 DOM 树时收听事件。
window.addEventListener("click", function(){alert('1: on bubble')}, false);
window.addEventListener("click", function(){alert('2: on capture')}, true);
警报框将按以下顺序弹出:
- 2:捕获
- 1:关于泡沫
真实世界的用例
Capture Event 将在 Bubble Event 之前发送,因此,如果你在捕获阶段收听事件,则可以确保首先收听事件。
如果你正在侦听父元素上的 click 事件以及其子元素上的另一个事件,则可以先侦听子节点,也可以先侦听父节点,具体取决于你更改 useCapture 参数的方式。
在冒泡中,子事件首先被调用,在捕获中,父项首先被调用
HTML:
<div id="parent">
<div id="child"></div>
</div>
使用 Javascript:
child.addEventListener('click', function(e) {
alert('child clicked!');
});
parent.addEventListener('click', function(e) {
alert('parent clicked!');
}, true);
将 true 设置为父 eventListener 将首先触发父侦听器。
结合 e.stopPropagation()
,你可以阻止事件触发子事件侦听器/或父事件。 (更多关于下一个例子中的内容)