JS事件委托(事件代理)

2020-02-27  本文已影响0人  寒o_0

什么是事件委托?

举个例子,我们要实现点击 li 时打印其id

<ul id="myList">
  <li id="a">aaa</li>
  <li id="b">bbb</li>
  <li id="c">ccc</li>
</ul>

一般我们会给每一个li绑定一个事件处理函数

let myList = document.getElementById('myList')
let oLi = myList.getElementsByTagName('li')

for (let i = 0; i < oLi.length; i++ ) {
  oLi[i].addEventListener('click', function () {
    console.log(this.id)
  })
}

这种方法存在两个问题:

  1. 如果有很多 li,大量的事件处理程序会占用不少内存,绑定事件处理程序也需要访问很多次DOM,页面的整体性能将大大降低
  2. 后续新增的li没有绑定事件处理程序

事件委托就是解决这两个问题的一种方案,方式如下

let myList = document.getElementById('myList')

myList.addEventListener('click', function (e) {
  console.log(e.target.id)
})

事件委托的原理:

由于事件冒泡的机制,当我们点击li时,ul也会触发click事件,所以我们只需给ul绑定事件处理程序,就能监听所有li的click事件,而且是动态的,新增的li也能被监听;
但如何判断我们点击的是哪个 li 呢?Event对象提供了一个属性叫target,该属性包含事件的目标,也就是事件源,通过target可以得到事件的目标节点

常见的问题

1.如果每个li被点击的效果不一样,怎么办呢?

let myList = document.getElementById('myList');

myList.addEventListener('click', function (e) {
  let target = e.target
  if (target.nodeName.toLowerCase() === 'li') {
    switch(target.id) {
      case 'a': 
        console.log('a1');
        break;
      case 'b': 
        console.log('b2');
        break;
      case 'c': 
        console.log('c3');
        break;
    }
  }
})

2.如果li还有其他子节点怎么办?

<ul id="myList">
  <li id="a">
    <p>aaa</p>
  </li>
  <li id="b">
    <div>
      <p>bbb</p>
    </div>
  </li>
  <li id="c">ccc</li>
</ul>
let myList = document.getElementById('myList');

myList.addEventListener('click', function (e) {
  let target = e.target
  while (target !== myList) {
    if (target.tagName.toLowerCase() === 'li') {
      console.log(target.id)
      break
    }
    target = target.parentNode
  }
})

核心是通过while和target.parentNode(或parentElement)遍历事件流上的所有节点

JQuery的事件委托

$('#myList').on('click', 'li', function (e) {
  console.log(e.currentTarget.id)
  console.log(this.id)
})

on()方法语法如下
$(selector).on(event,childSelector,data,function)
childSelector:指定被代理的后代元素

这里的this和e.currentTarget指向childSelector

React 与 Vue 中是否有必要使用事件委托?

React 内部自定义了一套事件系统,利用事件委托机制在Document上统一监听DOM事件,再根据触发的target将事件分发到具体的组件实例,所以无需我们再使用事件委托

Vue 内部没有做事件委托,但做了一些优化,在 v-for 中每个侦听器都使用同一个回调,并会自动处理监听器的创建和销毁,所以一般情况下没有必要使用事件委托

参考:
js中的事件委托或是事件代理详解 - 凌云之翼 - 博客园
谈谈React事件机制和未来(react-events) - 荒山的文章 - 知乎
https://www.runoob.com/jquery/event-on.html
https://forum.vuejs.org/t/is-event-delegation-necessary/3701/3
《JavaScript高级程序设计》

上一篇下一篇

猜你喜欢

热点阅读