「代理」能夠理解爲代勞或者幫忙,或者相似你的事就是個人事這種樂於助人的良好品質。JavaScript 也有許多事件要處理,接着就演示下,如何在 DOM 元素中選出一位熱心腸的好同窗。javascript
想象一個平平無奇的表格,像下面這樣:html
需求是點擊刪除按鈕可刪除單條數據。java
用 JavaScript 實現點擊刪除的操做,須要監聽按鈕點擊事件,在事件點擊回調中,發送刪除數據的請求,請求成功後從新渲染表格。函數
基本款的實現大概以下:性能
HTML:ui
...
<tr>
<td>1</td>
<td>漢堡</td>
<td>
<button data-id="1">刪除</button>
</td>
</tr>
...
複製代碼
JavaScript:this
const deleteItem = (id) => {
// 依據 ID 刪除數據。
}
let btnList = document.getElementsByClassName('btn')
Array.prototype.forEach.call(btnList, (btn) => {
btn.addEventListener('click', function (e) {
deleteItem(this.dataset.id)
})
})
複製代碼
上面的代碼給每一條刪除按鈕都添加了點擊事件的回調。上面的代碼利用自定義屬性 data-id
將單條數據的 ID
傳給數據的刪除按鈕,而後在點擊事件的回調中經過 dataset
屬性取得,將其傳遞給處理刪除的函數。spa
這樣能夠實現上面的上面所說的刪除功能。prototype
但是這麼寫,當數據一多,將會對頁面性能形成影響。設計
參考《JavaScript 高級程序設計》:
首先,每一個函數都是對象,都會佔用內存;內存中的對象越多,性能就越差。其次,必須事先指定全部事件處理程序而致使的 DOM 訪問次數,會延遲整個頁面的交互就緒時間。
面對這種情況,解決方案是事件代理。
先看代碼:
let table = document.getElementById('table')
table.addEventListener('click', function (e) {
if (e.target.tagName === 'BUTTON') {
deleteItem(e.target.dataset.id)
}
})
複製代碼
上面的代碼,首先在回調中經過事件對象 e
的 target
屬性取得觸發點擊事件的元素,而後經過 tagName
判斷這個元素是否是按鈕,若是是的話,就進行刪除操做。
只添加了一次事件處理函數,就實現了功能。
這裏利用的是事件流的事件冒泡。
事件流有三個階段,分別是事件捕獲階段,處於目標階段,還有事件冒泡階段。
按鈕點擊事件的事件流以下:
上圖省略了 tr
,td
, tbody
這些元素,只用關鍵的元素來展現事件流。
「事件捕獲」是不太精確的目標先接收到事件,首先是文檔,而後再到 body
,逐漸精確到最具體的節點 button
.
當 button
接收到事件,即爲「處於目標階段」。
以後,事件再向外層逐級傳播到不太具體的節點,這即是「事件冒泡階段」。
假若有個外層元素稱爲 box
, 它裏面包含了一個按鈕 btn
:
給 box
和 btn
註冊點擊事件,代碼以下:
box.addEventListener('click', function (e) {
console.log('box')
})
btn.addEventListener('click', function (e) {
console.log('button')
})
複製代碼
點擊 btn
:
能夠看到,首先打印的是 'button'
,接着纔是 box
. 由於 addEventListener
默認是在冒泡階段執行回調函數的。這裏,最具體的 btn
接受到事件以後,向外層冒泡,box
才能接受到事件。
改一下代碼:
box.addEventListener('click', function (e) {
console.log('box')
}, true)
btn.addEventListener('click', function (e) {
console.log('button')
})
複製代碼
這裏給 box
的 addEventListener
傳入了第三個參數 true
, 表示要在捕獲階段處理 box
的點擊事件。
addEventListener
第三個參數形參爲 useCapture
, 表示是否在捕獲階段處理事件,默認爲 false
.
運行一下:
這樣一改,首先打印的就是 'box'
, 由於事件具體節點是 btn
,而事件要從不太具體的節點,通過捕獲階段,才肯定到 btn
. 在捕獲階段,外層 box
要比 btn
早接受到事件,因此先執行了 box
捕獲階段的事件處理函數。
事件代理的代碼中,註冊點擊事件的是 table
元素,發生點擊事件最具體的節點是 button
. 事件在 button
發生後,因爲冒泡,會傳播到 table
, 從而觸發 table
上事先註冊的回調函數。因此,用事件冒泡實現事件代理,能夠只給熱心腸同窗 table
添加點擊事件回調,而沒必要勞煩表格中的每一個 button
.
在相似的場景下,可利用事件代理,來減小冗餘,節約資源。