# 事件委派(Event Delegation)
**事件委派(Event Delegation)或稱事件委託。**
是一種將事件監聽器直接添加到父元素(而非子元素)的技術。
每當父元素底下的子元素被觸發時,事件監聽器就會因為DOM的事件傳遞機制-事件冒泡而被觸發。
**事件委派的特色:**
減少記憶體空間:因為只需綁定一個處理程序在父元素上,我們不必在為每個子元素上綁定事件,或從已刪除的元素或新增的元素中綁定事件監聽處理器。
## 先複習一下何為DOM的事件傳遞機制?(~~趕時間的人可以直接跳過這段~~~)
**事件傳遞機制(Event Propagation)**,或說**事件流程/事件流(Event Flow)**
先假設網頁有一個按鈕 html結構如下:
```
<body>
<div id="outer">
<button id="btn">click</button>
</div>
</body>
```

我們點擊button的時候,其實也點到了body和div標籤,在看遠一點也可以說實際上我們點到整個網頁。
事件傳遞機制會探討的是:事件觸發時網頁接收事件的順序。
事件傳遞機制可分為兩種模式:
* **事件捕獲(Event Capturing)**
從被觸發事件的元素結點開始,逐漸往下層傳遞。
上面的例子來看,點擊button後,事件捕獲的機制下觸發順序會是:
1. body 標籤
2. div 標籤
3. button 標籤
* **事件冒泡(Event Bubbling)**
從被觸發事件的元素結點開始,~~啵啵啵~~逐漸往上層傳遞。
事件冒泡的機制下觸發順序會是:
1. button 標籤
2. div 標籤
3. body 標籤
要檢驗事件傳遞機制,會用addListener()綁定事件。
我們經常使用addListener()前兩個函數,實際上還有第三個函數:布林值。
這個布林值決定了事件是以「捕獲(true)」或「冒泡機制(false)」執行。
> addListener(事件名稱, 事件處理器function, 布林值)
若不指定,**預設會是false,也就是冒泡機制**。
我們用剛剛的按鈕結構,實際用addListener()綁定各三個物件,使用捕獲機制和冒泡機制一起執行以下程式:
```+
// 冒泡機制
document.getElementById("btn").addEventListener('click',()=>{
console.log("Bubble, button clicked")
},false)
document.getElementById("outer").addEventListener('click',()=>{
console.log("Bubble, div clicked")
},false)
document.body.addEventListener('click',()=>{
console.log("Bubble, body clicked")
},false)
// 捕獲機制
document.getElementById("btn").addEventListener('click',()=>{
console.log("Capture, button clicked")
}, true)
document.getElementById("outer").addEventListener('click',()=>{
console.log("Capture, div clicked")
}, true)
document.body.addEventListener('click',()=>{
console.log("Capture, body clicked")
}, true)
```
執行程式得到以下順序:
"Capture, body clicked"
"Capture, div clicked"
"Capture, button clicked"
"Bubble, button clicked"
"Bubble, div clicked"
"Bubble, body clicked"
先捕獲後冒泡。
### 如何阻止事件傳遞機制?
當畫面處理變得很複雜,可以使用**停止事件(stopPropagation)**,阻止當前事件繼續進行捕捉或冒泡的傳遞。
以上面的程式碼中作測試,我們在捕獲機制的outer ID的點擊事件裡,新增停止事件:
```+
document.getElementById("outer").addEventListener('click',(e)=>{
e.stopPropagation()
console.log("Capture, div clicked")
}, true)
```
然後執行程式,結果會得到:
"Capture, body clicked"
"Capture, div clicked"
程式執行到捕獲機制的div標籤時就停止傳遞了。
## 回到重點,事件委派的舉例:
假設我們有三個按鈕,事先配給他們屬性,html結構如下:
```+
<div id="menu">
<button data-key="add">新增</button>
<button data-key="update">修改</button>
<button data-key="delete">移除</button>
</div>
```

我們可以僅為父元素的menu ID綁定事件偵聽器如下:
```+
document.getElementById("menu")
menu.addEventListener("click",()=>{
console.log("clicked")
})
```
於是我們無論點哪個按鈕,都可以被偵測到點擊事件:
"clicked"
"clicked"
"clicked"
接著我們會想知道到底使用者點到的是哪個按鈕,作出區隔。
我們可以利用事件物件來取得目標物件的屬性,識別出點擊的當下是哪個屬性,對應到該物件。
```+
document.getElementById("menu")
menu.addEventListener("click",(e)=>{
let key = e.target.getAttribute("data-key")
console.log(key)
})
```
接著點新增 修改 移除 按鈕就能對應到各自的屬性名稱:
"add"
"update"
"delete"
**所以優點!!我們不必為每個按鈕(子元素)都綁定事件監聽器。
也可以知道,如果要在menu ID這個父元素底下新增或移除按鈕時,也都不用再另外綁定事件或移除事件了。**
#### 這就是事件委派,我們僅將事件監聽器綁在父元素上,每當父元素底下的子元素被點擊時,父元素的事件監聽器就會因為事件冒泡而被觸發。
---
參考資料:
彭彭直播 - 事件傳遞機制和事件委託 Event Propagation & Event Delegation
https://www.youtube.com/watch?v=3cTdztTmtL0&t=6621s
重新認識 JavaScript: Day 14 事件機制的原理
https://ithelp.ithome.com.tw/articles/10191970?sc=iThelpR
Explain event delegation
https://github.com/yangshun/front-end-interview-handbook/blob/master/contents/en/javascript-questions.md#explain-event-delegation