事件分為三個階段,
1:Capturing Phase 捕獲階段
2:Target Phase 傳遞到元素本身
3:Bubbling Phase 冒泡階段
完整事件流
事件冒泡
由事件目標依序向外傳遞,過程中觸發各別元素的監聽事件。(逐層往上傳遞,直到整個網頁的根節點,也就是 document->Window。)
注意:大部分事件都會冒泡
範例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event Bubbling</title>
</head>
<<body id ="body">
<div id = "lay1">
<div id = "lay2">
<button id="clickMe">Click Me</button>
</div>
</div>
<script>
let button = document.getElementById('clickMe');
let div1= document.getElementById('lay1');
let div2= document.getElementById('lay2');
button.onclick = function() {
console.log('Button active');
};
div1.onclick=function(){
console.log("div1 active");
}
div2.onclick=function(){
console.log("div2 active");
}
document.body.onclick = function() {
console.log('body active');
};
document.onclick = function() {
console.log('document active');
};
window.onclick = function() {
console.log('window active');
};
</script>
</body>
</html>
以上架構來講的話,冒泡事件順序是
button>lay2>lay1>body>document>window
輸出結果

事件捕獲
另一種事件流稱為事件捕獲,與事件冒泡相反,DOM 的事件會從(window) 開始往下尋找目標 (target)。是一個找尋過程
addEventListener第三個參數 useCapture,根據文檔設為True時,在事件冒泡時不會啟動函數等於是在事件捕獲階段就會啟動函數,預設值為False,則為在事件冒泡時才會啟動函數。
addEventListener(type, listener);
addEventListener(type, listener, options);
addEventListener(type, listener, useCapture);
//刪除下列三行
document.body.onclick=function()
console.log('window active');
};
//改為下列三行
document.body.addEventListener('click', function() {
console.log('body active');
},true);
事件觸發結果會是?
注意:感謝你的注意
冒泡還是捕獲?
程式會執行哪個?答案是兩個都會執行,但是在實際執行事件操作時,通常是由冒泡來執行,且大部分都事件會冒泡
事件代理
假設今天需要獲取某個li的資料,必須監聽每個li,如果li數量不多的情況下,看不太出區別,但只要數量一多使用平常的方法必會降低效能,所以有一種方法叫做事件代理,這邊是指一種寫法而不是一種語法,是用父級的元素做事件處理,當子級的元素被觸發,根據事件冒泡的流程,會跑到父級,這麼設定的話父級被點擊也會被觸發,不用擔心,可以使用target設定來規避這個問題。
w3c事件代理
範例
<ul id="parent-list">
<li id="item_1">Item 1</li>
<li id="item_2">Item 2</li>
<li id="item_3">Item 3</li>
<li id="item_4">Item 4</li>
<li id="item_5">Item 5</li>
<li id="item_6">Item 6</li>
</ul>
// Get the element, add a click listener...
document.getElementById("parent-list").addEventListener("click", function(e) {
// e.target is the clicked element!
// If it was a list item
if(e.target && e.target.nodeName == "LI") {
// List item found! Output the ID!
alert("List item " + e.target.id.replace("item_", "") + " was clicked!");
}
});