简述DOM事件委托

190 阅读3分钟

事件委托,也称为事件代理,一般来说,它是把一个或一组元素的事件委托到其父层或者更外层的元素上,《JavaScript高级程序设计》书上描述事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件,下面来看一个取快递的例子:

假设班上有五十个同学需要去取快递,如果每一个人都需要自己去取的话,那么不仅浪费时间,而且还会占据快递点大量的空间,而如果这五十个人的快递都指定班长一个人去领,班长再根据快递上的名字进行分发,那么其效率就能大大提高。

上述的例子中班长就代理了五十名同学取快递的事件,使得五十个人的事件就委托给了一个人去做,DOM事件委托也就是根据这样的原理设计的,它有两大优点,一是可以节约内存,二是可以处理动态元素。

1.节约内存

如取快递的例子,将五十个人的事情都委托给班长一个人做,那么就能大大节约快递点的空间,不需要让五十名同学都来到快递点。如果现在有几十个 li 元素,给每一个 li 元素都设置点击事件,那么就需要设置几十个监听器,而如果根据事件冒泡机制,将事件监听设置在其父元素中,监听的过程中根据 e.target 得到当前触发事件的元素,再对该元素进行操作,这样就能大大减少内存的占用,因为只需要设置一个监听器,如下所示:

<body>
 <ul>
 <li>我是li 1</li>
 <li>我是li 2</li>
 <li>我是li 3</li>
 <li>我是li 4</li>
 <li>我是li 5</li>
      ......
 </ul>
 <script>
 let ul = document.querySelector('ul');
        ul.addEventListener('click', e => {
            console.log(e.target.innerText);
        })
 </script>
</body>

2.处理动态元素

如果某一个元素并不是固定存在的,而是动态变化的,那么你就需要在元素存在的时候绑定事件,不存在的时候解除绑定的事件,如果不解除会浪费内存,这样就会很麻烦,而如果使用事件委托,将事件绑定在动态元素的父元素上,那么就可以不用去处理元素存不存在的问题了,如下例所示,能够访问暂时还不存在的span元素:

<body>
 <div id="parent"></div>
 <script>
 let parent = document.getElementById('parent')
        setTimeout(() => {
 let span = document.createElement('span');
            span.innerText = '我是子元素';
            parent.appendChild(span);
        }, 1000);
        parent.addEventListener('click', e => {
            console.log(e.target)
        })
 </script>
</body>

3.事件委托的局限性

事件委托也有一定的局限性,比如 focus、blur 之类的事件本身没有事件冒泡机制,所以无法采用事件委托,对于mousemove、mouseout 这类虽然有事件冒泡的事件,但是需要不断通过位置去计算定位,这样就比较消耗性能,因此也不适合使用事件委托。

4.事件委托函数

最后附上一个比较完整的事件委托的函数,在封装该函数的时候,主要要考虑到这种情况:假设你要给多个 li 元素监听点击事件,那么就将监听放在其父元素 ul 中,但是如果 li 元素中还嵌套有 span 元素,这样当你点击的时候就会很容易点击到 span 元素上,而不是 li 元素,所以 e.target 就应该逐层向上传递一下,看是否有匹配的 li 元素,当 e.target 等于本身监听的 ul 元素的时候就表示没有符合的元素,于是便结束循环:

function delegate(element, eventType, selector, fn) {
  element.addEventListener(eventType, e => {
    let el = e.target
    while (!el.matches(selector)) {
      if (el === element) {
        el = null
        break
      }
      el = el.parentNode
    }
    el && fn.call(el, e, el)
  })
  return element
}

欢迎大家留言讨论。

本文参考: zhuanlan.zhihu.com/p/26536815