iT邦幫忙

2021 iThome 鐵人賽

DAY 22
0
自我挑戰組

邊工作邊進行前端學習之旅系列 第 22

[Day 22] JS - 事件委派 Delegation

前言

不知不覺每日的挑戰發文活動,也進行到22天了,這件事已經變成每日必做的事,像平日就是工作結束後,就是點開iT鐵人賽進行發文,而假日的話則是起床後,首要進行的事項,有時睡前還要確認下今天是不是已經發文了,就是要堅持的將挑戰完成!

今天要介紹的事件委派 Delegation 和 [Day 17] JS - 冒泡事件 (Event Bubbling) 所介紹的基礎觀念有關係,可以藉此再一併複習一次。

事件委派簡單說,在父層元素綁定一事件,當觸發子層的元素事件時,透過冒泡特性觸發父層的事件。

學習資源分享

彭彭 - JavaScript 面試:事件傳遞機制和事件委託 Event Propagation & Event Delegation,從事件傳遞的基礎介紹,如:捕獲 (Capture) 和冒泡 (Bubble) 階段,而到事件委派的運作,以按鈕的區分、選項切換效果等範例說明。

透過下方的例子來說明

遇到可使用事件委派的情況:

我們希望 ul 下的列表,透過點擊1111會印出111、點擊2222會印出2222、點擊3333會印出3333,預想會認為要在個別元素上去建立監聽事件。

  <div id="box">
    <ul id='my-list'>
      <li >1111</li>
      <li >2222</li>
      <li >3333</li>

    </ul>
  </div>
  • 原本撰寫方式會需要利用迴圈,來取得li元素,並一個個綁定事件
// 取得容器
    var myList = document.getElementById('my-list');
    // 分別為 li 綁定事件
    if (myList.hasChildNodes()) {
      for (let i = 0; i < myList.childNodes.length; i++) {

        // nodeType === 1 代表為實體 HTML 元素
        if (myList.childNodes[i].nodeType === 1) {
          myList.childNodes[i].addEventListener('click', function () {
            console.log(this.textContent);
          }, false);
        }

      }
    }
  • 此外我們若想在列表中,新增名為Hello world!的li
// 建立新的 <li> 元素
    var newList = document.createElement('li');

    // 建立 textNode 文字節點
    var textNode = document.createTextNode("Hello world!");

    // 透過 appendChild 將 textNode 加入至 newList
    newList.appendChild(textNode);

    // 透過 appendChild 將 newList 加入至 myList
    myList.appendChild(newList);

此時,會發現一個問題,就是後來才新增的 newList 節點並不會有 click 事件的註冊。因為新增新的li,而需要再另外建立新的監聽事件,此方式變得繁雜且容易出錯。

所以...就有了事件委派!

  • 將監聽事件改由父層my-list執行,透過檢查事件的目標並判斷e.target目標元素為li,來判斷並獲取li,再執行要click的事宜。
  • 所以在父層建立click事件,當子層項目被點擊時,點擊事件就會從子層冒泡。
  • 此外,透過此方式,即便後續建立新的li元素,也會有click的效果。
// 取得容器
    var myList = document.getElementById('my-list');

    // 改讓外層 myList 來監聽 click 事件
    myList.addEventListener('click', function (e) {

      // 判斷目標元素若是 li 則執行 console.log
      if (e.target.tagName.toLowerCase() === 'li') {
        console.log(e.target.textContent);
      }

    }, false);

    // 建立新的 <li> 元素
    var newList = document.createElement('li');

    // 建立 textNode 文字節點
    var textNode = document.createTextNode("Hello world!");

    // 透過 appendChild 將 textNode 加入至 newList
    newList.appendChild(textNode);

    // 透過 appendChild 將 newList 加入至 myList
    myList.appendChild(newList);

  

其他應用方式,取得data-* attribute

  • 透過委派方式,來建立取的子層各元素data屬性內的資料
  • 使用 jQuery 的.delegate()
    • 將監聽事件建立在 ul 上,監測底下的 li 並執行 li 的點擊時,以顯示 data-id。
    • 此外需注意,我們在ul的外層還有一個id='box'的 div,它有綁定一個事件,所以必須要在 ul 的監聽事件,增加 event.stopPropagation(); 避免冒泡渲染到外層。
  <div id="box">
    <ul id='my-list'>
      <li data-id="li-1">001</li>
      <li data-id="li-2">002</li>
      <li data-id="li-3">003</li>
      <li data-id="li-4">004</li>

    </ul>
  </div>
<script>
    $(function () {
      $("#my-list").delegate("li", "click", function (event) {
        event.stopPropagation();
        console.log($(this).data("id"))
      });
      $('#box').click(function (event) {
        console.log('I am box');
      })

    })

  </script>

說說使用事件委派的好處:

1.讓語法更簡潔,減少建立多個且繁瑣的監聽事件。
2.可以方便地些改元素,不需因元素的更動,還需而外修改監聽事件。
3.使增進擷取元素的效能,且減少內存的佔用。


上一篇
[Day 21] 閉包 (Closure)是什麼?
下一篇
[Day 23] JS - 陣列 (Array) 常用方法介紹
系列文
邊工作邊進行前端學習之旅30

尚未有邦友留言

立即登入留言