[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.使增进撷取元素的效能,且减少内存的占用。


<<:  [Day10]-字典2

>>:  Day10 while回圈

[ 卡卡 DAY 9 ] - React Native style 优化、共用没烦恼!window.innerWidth 在 RN 怎麽处理?

有没有想过如果我要调整整个 App 的主色或字体大小,要一个一个元件改有多累? 有没有想过只要改一...

【Day 18】Complexity & Graphs

接下来我们要针对复杂度做介绍,首先要说的就是高手们常常说的「Big O」! 但是到底什麽是 big ...

10. CI x Github Action

CI 持续整合。 为什麽要 CI 呢? 想想我们前面写了那麽辛苦的自动测试,结果有人不跑测试就上传。...

Day16 Combine 03 - Subscriber

Subscriber 与Publisher 相对应,是观察者模式中的Observer,Publish...

Day.17 「如果基本型别是商品,那物件型别就是购物袋」 —— JavaScript 物件型别

前面有介绍了基本型别,基本型别有 string、number、boolean、null、undef...