事件委托

225 阅读2分钟

为什么会有事件委托?

  1. 一个列表中有多个li,都需要绑定监听函数,用for循环能做到,但性能差。
  2. 目前不存在的元素怎么添加监听函数?用JS一直监听元素是否出现再绑定吗?过程复杂,性能也差。

什么是事件委托?

事件委托、事件代理都是同一个东西。委托就是让他人帮你做你本应该做的事。事件委托就是子元素的事件监听委托给父元素、祖先元素监听。

原理

上一篇文章讲了事件模型,事件委托就是利用了事件模型的运行机制(window->最里层元素->window),将事件的监听委托给祖先元素。

实现一

 <body>
    <ul id="demo">
      <li>li1</li>
      <li>li2</li>
      <li>li3</li>
      <li>li4</li>
      <li>li5</li>
      <li>li6</li>
      <li>li7</li>
      <li>li8</li>
      <li>li9</li>
      <li>li10</li>
    </ul>
    <script>
      demo.addEventListener("click", () => {
        console.log("没有给li绑定事件,但点击了li,都会执行demo的监听函数");
      });
    </script>
  </body>

但该实现有个问题,假如ul里面有其他元素呢?(其实ul里面最好只放li元素,可以想成div里面有多种元素,只想其中一种元素点击了才执行监听函数。)那么就需要加些代码判断了。

实现二

target:触发事件的元素
currentTarget:绑定了监听函数的元素
tagName:可获取元素的标签名
toLowerCase():将字符串转成小写形式

修改后的代码:

 demo.addEventListener("click", (e) => {
      if (e.target.tagName.toLowerCase() === "li") {
          console.log("没有给li绑定事件,但点击了li,都会执行demo的监听函数");
      }
});

然而这段代码还有有点问题。 HTML代码部分改成

  <ul id="demo">
      <li><span>li1</span></li>
      <li><span>li2</span></li>
      <li><span>li3</span></li>
      <li><span>li4</span></li>
      <li><span>li5</span></li>
      <li><span>li6</span></li>
      <li><span>li7</span></li>
      <li><span>li8</span></li>
      <li><span>li9</span></li>
      <li><span>li10</span></li>
</ul>

点击li中的文字,发现控制台没有打印任何东西。是事件没有传播上去吗?不是,而是target判断出现了问题,此时target是span元素,自然不会进入if语句当中。当span元素又的确是li的一部分,该怎么让他执行呢?

实现三

 demo.addEventListener("click", (e) => {
    let el = e.target;
    while (!(el.tagName.toLowerCase() === "li")) {
      if (el === demo) {  // 必须先判断是否等于祖先元素,否则死循环。
        el = null;
        break;
      } else {
        el = el.parentNode;
      }
    }
    el && console.log("没有给li绑定事件,但点击了li,都会执行demo的监听函数");
  });

当我们发现target不是li时,通过递归查找target的祖先元素是不是li。
但要注意递归的范围。target <= li < demo 跳出递归后也要判断一下el是否为空,即有没有找到祖先元素,找到了才执行log语句。

总结

我们可以发现事件委托说简单也简单,说难也难。原理利用了事件模型的运行机制,但是利用的过程中要考虑多方面的因素。实现三是较好的事件委托,考虑了target的子元素点击和target兄弟元素点击下的触发情况。读者可以将实现三封装成一个函数,当你封装好了,也就可以说掌握了事件委托。最后,谢谢您的阅读!