JavaScript 事件代理簡單例子

「代理」能夠理解爲代勞或者幫忙,或者相似你的事就是個人事這種樂於助人的良好品質。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)
  }
})
複製代碼

上面的代碼,首先在回調中經過事件對象 etarget 屬性取得觸發點擊事件的元素,而後經過 tagName 判斷這個元素是否是按鈕,若是是的話,就進行刪除操做。

只添加了一次事件處理函數,就實現了功能。

這裏利用的是事件流的事件冒泡。

原理

事件流

事件流有三個階段,分別是事件捕獲階段,處於目標階段,還有事件冒泡階段。

按鈕點擊事件的事件流以下:

上圖省略了 tr ,td, tbody 這些元素,只用關鍵的元素來展現事件流。

「事件捕獲」是不太精確的目標先接收到事件,首先是文檔,而後再到 body,逐漸精確到最具體的節點 button.

button 接收到事件,即爲「處於目標階段」。

以後,事件再向外層逐級傳播到不太具體的節點,這即是「事件冒泡階段」。

冒泡與捕獲演示

假若有個外層元素稱爲 box, 它裏面包含了一個按鈕 btn

boxbtn 註冊點擊事件,代碼以下:

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')
})
複製代碼

這裏給 boxaddEventListener 傳入了第三個參數 true, 表示要在捕獲階段處理 box 的點擊事件。

addEventListener 第三個參數形參爲 useCapture, 表示是否在捕獲階段處理事件,默認爲 false.

運行一下:

這樣一改,首先打印的就是 'box', 由於事件具體節點是 btn,而事件要從不太具體的節點,通過捕獲階段,才肯定到 btn. 在捕獲階段,外層 box 要比 btn 早接受到事件,因此先執行了 box 捕獲階段的事件處理函數。

總結

事件代理的代碼中,註冊點擊事件的是 table 元素,發生點擊事件最具體的節點是 button. 事件在 button 發生後,因爲冒泡,會傳播到 table, 從而觸發 table 上事先註冊的回調函數。因此,用事件冒泡實現事件代理,能夠只給熱心腸同窗 table 添加點擊事件回調,而沒必要勞煩表格中的每一個 button .

在相似的場景下,可利用事件代理,來減小冗餘,節約資源。

相關文章
相關標籤/搜索