淺析 JavaScript 中的事件委託

做者:Dmitri Pavlutin

翻譯:瘋狂的技術宅javascript

原文:https://dmitripavlutin.com/ja...html

未經容許嚴禁轉載前端

爲何要進行事件委託?

首先實現一個小功能:在單擊 HTML 的按鈕後,把消息輸出到控制檯。java

爲了實現這個小功能,你須要選擇按鈕,而後再用 addEventListener() 方法來附加事件監聽器:程序員

<button id="buttonId">Click me</button>

<script>
  document.getElementById('buttonId')
    .addEventListener('click', () => console.log('Clicked!'));
</script>

以上就是偵聽單個元素(尤爲是按鈕)上事件的方式。面試

若是須要監聽多個按鈕上的事件呢?下面是一種可能的實現:算法

<div id="buttons">
  <button class="buttonClass">Click me</button>
  <button class="buttonClass">Click me</button>
  <!-- buttons... -->
  <button class="buttonClass">Click me</button>
</div>

<script>
  const buttons = document.getElementsByClassName('buttonClass');
  for (const button of buttons) {
    button.addEventListener('click', () => console.log('Clicked!'));
  }
</script>

你能夠在 Codesandbox 上查看它是怎樣工做的segmentfault

按鈕列表被迭代爲 for (const button of buttons) ,而且每一個按鈕都被附加了一個新的偵聽器。另外在列表中的按鈕被添加或刪除後,你必須還要手動刪除或附加事件監聽器。服務器

有沒有更好的方法?微信

幸運的是,若是咱們使用「事件委託」模式的話,偵聽多個元素上的事件只須要一個事件偵聽器。

事件委託使用事件傳播機制的細節。想要要了解事件委託的工做原理,應該先了解什麼是事件傳播。

事件傳播

當你單擊下面 html 中的按鈕時:

<html>
  <body>
    <div id="buttons">
      <button class="buttonClass">Click me</button>
    </div>
  </body>
</html>

點擊事件會觸發多少個元素?毫無疑問,按鈕自己會收到單擊事件。並且全部按鈕的祖先,甚至包括 documentwindow 對象也會收到。

點擊事件的傳播分三個階段:

  1. 捕獲階段 —— 從windowdocument 和根元素開始,事件向下擴散至目標元素的祖先
  2. 目標階段 —— 事件在用戶單擊的元素上觸發
  3. 冒泡階段——最後,事件冒泡經過目標元素的祖先,一直到根元素 documentwindow

JavaScript事件傳播

image.png

addEventListener 方法的第三個參數 captureOrOptions

element.addEventListener(eventType, handler[, captureOrOptions]);

使你能夠捕獲來自不一樣階段的事件。

  • 若是缺乏 captureOrOptions 參數,或者參數爲 false{capture:false},那麼偵聽器將捕獲目標(target)和 冒泡階段(bubble phases)的事件
  • 若是參數是 true{capture:true},那麼偵聽器將會偵聽捕獲階段(capture phase)的事件。

經過下面的代碼,你會偵聽到在 <body> 元素上發生的捕獲階段的點擊事件:

document.body.addEventListener('click', () => {
  console.log('Body click event in capture phase');
}, true);

在這個 Codesandbox 演示中,單擊按鈕時,你能夠在控制檯中查看事件的傳播方式。

那麼事件傳播是怎樣幫助捕獲多個按鈕事件的呢?

該算法很簡單:把事件偵聽器附加到按鈕的父級,並在單擊按鈕時捕獲冒泡事件。這就是事件委託的工做方式。

3.事件委託

讓咱們用事件委託來捕獲多個按鈕上的點擊:

<div id="buttons"> <!-- Step 1 -->
  <button class="buttonClass">Click me</button>
  <button class="buttonClass">Click me</button>
  <!-- buttons... -->
  <button class="buttonClass">Click me</button>
</div>

<script>
  document.getElementById('buttons')
    .addEventListener('click', event => { // Step 2
      if (event.target.className === 'buttonClass') { // Step 3
        console.log('Click!');
      }
    });
</script>

打開Codesandbox 演示,而後單擊任意按鈕,你會看到 'Click!' 消息被記錄到控制檯。

事件委託的思想很簡單。你不須要把委託事件監聽器直接附加到按鈕上,而是能夠委託父監聽 <div id="buttons">。單擊按鈕時,父元素的偵聽器將會捕獲冒泡事件(還記得前面所說事件傳播嗎?)。

使用事件委託須要3個步驟:

步驟1:肯定要監視事件的元素的父級元素

在上面的例子中, <div id="buttons"> 是按鈕的父元素。

步驟2:把事件偵聽器附加到父元素

document.getElementById('buttons') .addEventListener('click', handler) 將事件偵聽器附加到按鈕的父元素。該事件偵聽器也會對按鈕單擊作出反應,由於按鈕單擊事件冒泡經過祖先元素(因爲事件傳播)。

步驟3:用 event.target 選擇目標元素

單擊按鈕時,將會用event 對象參數調用處理函數。屬性 event.target 訪問在其上調度了事件的元素,在例子中是一個按鈕:

// ...
.addEventListener('click', event => {
    if (event.target.className === 'buttonClass') {      
        console.log('Click!');
    }
});

順便說明一下,event.currentTarget 指向事件偵聽器直接附加到的元素。在例子中,event.currentTarget<div id="buttons">

如今,你能夠看到事件委託模式的好處:事件委託僅須要一個事件偵聽器,而沒必要像本文最初那樣將偵聽器附加到每個按鈕上。

總結

當發生點擊事件(或傳播的任何其餘事件)時:

  • 事件從 windowdocument、根元素向下傳播,並通過目標元素的祖先(捕獲階段);
  • 事件發生在目標(目標階段)上;
  • 最後,事件在目標祖先之間冒出氣泡,直到根元素 documentwindow(冒泡階段)。

該機制稱爲事件傳播

事件委託是一種有用的模式,由於你能夠只須要用一個事件處理程序就能偵聽多個元素上的事件。

使用事件委託須要三個步驟:

  1. 肯定要監視事件的元素的父級元素
  2. 把將事件偵聽器附加到父元素
  3. event.target 選擇目標元素

前端刷題神器

掃碼進入前端面試星球🌍,解鎖刷題神器,還能夠獲取800+道前端面試題一線常見面試高頻考點

173382ede7319973.gif


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索