美文网首页
JS事件委托(事件代理)

JS事件委托(事件代理)

作者: 寒o_0 | 来源:发表于2020-02-27 12:36 被阅读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高级程序设计》

    相关文章

      网友评论

          本文标题:JS事件委托(事件代理)

          本文链接:https://www.haomeiwen.com/subject/ucythhtx.html