# JS事件傳遞機制 事件分為三個階段, ``` 1:Capturing Phase 捕獲階段 2:Target Phase 傳遞到元素本身 3:Bubbling Phase 冒泡階段 ``` 完整事件流 ![](https://i.imgur.com/wdm0fn9.png) ## 事件冒泡 由事件目標依序向外傳遞,過程中觸發各別元素的監聽事件。(逐層往上傳遞,直到整個網頁的根節點,也就是 document->Window。) 注意:大部分事件都會冒泡 [w3c事件冒泡定義](https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow-bubbling) 範例 ``` <!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 ### 輸出結果 ![](https://i.imgur.com/Yvpdvvg.jpg) ## 事件捕獲 另一種事件流稱為事件捕獲,與事件冒泡相反,DOM 的事件會從(window) 開始往下尋找目標 (target)。是一個找尋過程 addEventListener第三個參數 useCapture,根據文檔設為True時,在事件冒泡時不會啟動函數等於是在事件捕獲階段就會啟動函數,預設值為False,則為在事件冒泡時才會啟動函數。 [mdn-addEventListener](https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener) [w3c事件捕獲定義](https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow-capture) 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事件代理](https://www.w3docs.com/learn-javascript/javascript-events.html) 範例 ``` <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!"); } }); ```