事件冒泡和捕獲

針對 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();
});

我們會看到以下輸出,bodyclick 處理程式從未觸發:

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 引數的方式。

StackOverflow 文件

在冒泡中,子事件首先被呼叫,在捕獲中,父項首先被呼叫

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