事件冒泡和捕獲
針對 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()
,你可以阻止事件觸發子事件偵聽器/或父事件。 (更多關於下一個例子中的內容)