js之必会技能:事件委托

  • Post author:
  • Post category:其他




什么是事件委托

事件委托也叫事件代理,代理一词很好理解了。就是把自己的事情委托给另一个人去做,打个比方,比如拿快递,像在学校中一般快递都是寄到学校的各个快递点的,学生下课后到指定的地点进行取件,我们把收快递的这个事件委托给快递点,这就是一个事件委托,当然了如果能够送到宿舍最好了O(∩_∩)O哈哈~

👉

阿勇的博客

👈



应用场景一:不改变 DOM 结构



功能需求

事件委托的出现肯定是有他的道理的,举个例子,如果我们想要实现这么一个需求:

  • 在页面上创建一个 ul 列表。
  • 在 ul 列表下有 5 个 li标签。
  • 给每个 li 标签一个文本内容。
  • 给 li 一个点击事件,当触发后 li 标签的背景颜色变为红色。

这里简单用代码实现一下:

  • html 部分:

    ul>li{这是第$个 li 标签}*5

    <ul>
        <li>这是第1个 li 标签</li>
        <li>这是第2个 li 标签</li>
        <li>这是第3个 li 标签</li>
        <li>这是第4个 li 标签</li>
        <li>这是第5个 li 标签</li>
    </ul>
    
  • js 部分

    // 获取所有的 li 标签
    let lis = document.getElementsByTagName('li'); 
    // 1. 遍历 lis 给每一个 li 添加点击事件改变背景颜色
    for (let item of lis) {
        item.addEventListener("click", () => { 
            item.style.background = "red";
        });
    }
    
  1. addEventListener 第三个参数可以控制事件冒泡和事件捕获(onclick不可以),默认为 false (冒泡), true (捕获)
  2. 事件处理 this与onclick一样
  3. 可以为某个元素绑定多个事件而不会覆盖之前绑定的处理程序 (按照顺序执行,onclick不可以)

功能已经实现,但是设想一下如果有 10000 个li标签,那么我们就需要遍历 10000 次,给每一个 li 添加点击事件,每一个函数就是一个对象,每个对象都会占用内存,对象越多,内存消耗就越大,所表现出来的网站性能就越差劲。所以我们应该减少 DOM 操作。所以事件委托派上用场了。



使用事件委托

使用代码实现一下:

  • html 部分和功能需求中相同,我们主要编写 js 部分代码

  • js 部分

    // 获取 ul 标签
    let ul = document.querySelector('ul');
    // 将单击事件委托给 li 的父级 ul
    ul.addEventListener('click',(e)=>{
        // 兼容性  获取事件源
        let ev = e || window.event;
        let target = ev.target || ev.srcElement; 
        // 进行大小写转换  ==比较
        if(target.nodeName.toLowerCase() == 'li'){
            target.style.background = 'red';
        }
    })
    

    这是我理解的事件委托使用到的一个场景。


    我们也可以通过 id 去完成事件委托

  • html部分

    <ul id="myUl">
      <li id="insert">insert</li>
      <li id="delete">delete</li>
      <li id="update">update</li>
      <li id="select">select</li>
    </ul>
    
  • js 部分

      // 获取 ul 标签
      let ul = document.querySelector('ul');
      // 将单击事件委托给 li 的父级 ul
      ul.addEventListener('click',(e)=>{
          // 兼容性  获取事件源
          let ev = e || window.event;
          let target = ev.target || ev.srcElement; 
          // 通过 switch 判断是点击了哪一个 li
          switch (target.id) {
              case "insert":
                  target.innerHTML = target.innerHTML + ":添加一个文本";
                  break;
              case "delete":
                  target.innerHTML = "";
                  break;
              case "update":
                  target.innerHTML = '这是修改后的文本';
                  break;
              case "select":
                  alert('这是查询的文本');
                  break;
          }
      })
    



应用场景二:改变 DOM 结构



功能需求

  • 在页面上创建一个 button 按钮。
  • 在页面上创建一个 ul 列表。
  • 在 ul 列表下有 5 个 li标签。
  • 给每个 li 标签一个文本内容。
  • 给 button 一个点击事件,当触发后生成一个 li 插入到 ul 下的最后一个 li 后边。
  • 给 ul 下的 li 一个点击事件,当触发后 li 标签的背景颜色变为红色。

这里简单用代码实现一下:

  • html 部分:

    ul>li{这是第$个 li 标签}*5

    <button>添加新标签</button>
    <ul>
        <li>这是第1个 li 标签</li>
        <li>这是第2个 li 标签</li>
        <li>这是第3个 li 标签</li>
        <li>这是第4个 li 标签</li>
        <li>这是第5个 li 标签</li>
    </ul>
    
  • js 部分

    // 获取 ul 标签
    let ul = document.querySelector("ul");
    // 获取 button 按钮
    let btn = document.querySelector("button");
    btn.addEventListener("click", () => {
        // 获取 lis 的长度
        let leg = lis.length;
        // 1. 创建一个 li 标签
        let li = document.createElement("li");
        // 2. 给创建的 li 标签添加文本内容
        li.textContent = `这是第${leg + 1}个 li 标签`;
        // 3. 把创建的 li 标签挂载到 document 中
        ul.appendChild(li);
    });
    // 获取 li 标签
    let lis = document.getElementsByTagName("li");
    for (let item of lis) {
        item.addEventListener("click", () => {
            item.style.background = "red";
        });
    }
    


    存在的问题:

    清楚的发现,已经在 DOM 结构中生成的 li 点击后可以改变背景颜色,但是通过 button 按钮插入的 li 当点击后没有效果,这里我理解为,button 的点击事件并没有事件复制过去。因为在程序一加载的时候,for循环已经遍历完成。



使用事件委托

事件委托就不用担心事件复制不了的问题了,使用代码实现一下:

  • html 部分和功能需求中相同,我们主要编写 js 部分代码

  • js 部分

    // 获取 ul 标签
    let ul = document.querySelector("ul");
    ul.addEventListener("click", e => {
        let ev = e || window.event;
        let target = ev.target || ev.srcElement;
        if (target.nodeName.toLowerCase() == "li") {
            target.style.background = "red";
        }
    });
    // 获取 button 按钮
    let btn = document.querySelector("button");
    btn.addEventListener("click", () => {
        // 获取 ul 下 li 的长度
        let leg = ul.children.length;
        console.log(leg);
        // 1. 创建一个 li 标签
        let li = document.createElement("li");
        // 2. 给创建的 li 标签添加文本内容
        li.textContent = `这是第${leg + 1}个 li 标签`;
        // 3. 把创建的 li 标签挂载到 document 中
        ul.appendChild(li);
    });
    

    其他应用场景:像 Element-ui 中 Dialog 效果可以使用到事件委托,有时间封装一下 Dialog 组件。



事件委托的优缺点



优点

  1. 减少内存消耗,减少事件绑定。
  2. 给动态添加的元素绑定事件。



缺点

  1. 部分事件如 focus、blur 等无冒泡机制,所以无法委托。
  2. 事件委托有对子元素的查找过程,委托层级过深,可能会被某一层阻止掉。
  3. 频繁触发的事件,不适合事件委托。



总结

​ 事件委托固然方便,但是我们也要在合适的场景使用它,以免事倍功半。



版权声明:本文为weixin_44013288原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。