JS 中的事件委托是什么?

事件委托是一种优化前端性能的技术,通过在父元素上绑定事件,利用事件冒泡机制处理子元素的事件。本文详细介绍了事件流的捕获和冒泡阶段,并通过实例解释了如何使用事件委托来处理好友列表的点击事件,以减少内存消耗和提高代码维护性。在实际开发中,尽管事件委托不总是必需的,但对于大型列表或动态内容,它能有效提升效率。

大家好,我是前端西瓜哥。今天我们来认识一下事件委托。

所谓事件委托,就是将原本应该在当前元素绑定的事件,放到它的祖先元素上,让祖先元素来委托处理。

事件流

事件流指从页面中接收事件的顺序,也可理解为事件在页面中传播的顺序。

事件流由两阶段组成:

  1. 捕获事件

  2. 冒泡事件

我们通常用 addEventListener 给元素添加事件:

document.querySelector('#card')addEventListener(
  'click',
  function (event) {
   console.log('div#card 冒泡点击', event);
 },
  false
);

第一个参数是事件名,第二个参数是事件响应函数,可以拿到当前的事件对象。

第三个参数是可选的,表示监听的是否为捕获阶段,false为冒泡阶段,也是默认值,true 为捕获阶段。我们常用的是冒泡阶段。

当我们点击元素时,就会执行这个函数。

假设我们的 DOM 结构如下:

<html>
  <head>
    <title>前端西瓜哥</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div id="app">
      <div id="box-1">
        <div id="card">card</div>
      </div>
      <div id="box-2"></div>
    </div>
  </body>
</html>

现在我们点击 card 文字时,DOM 就会产生事件流。

事件流首先会进入 捕获阶段,从根节点往目标元素(div#card)移动,依次经过为:

  • window

  • document(文档根元素,在 HTML 中没有显式声明)

  • document.documentElement(<html>

  • document.body(<body>

  • 目标元素 div#card

和调用事件对象的 event.composedPath() 方法拿到的 事件路径 类似。

图片

image-20220715213358879

window 看起来是个全局变量,但它也是可以绑定事件的,比如窗口大小改变的 resize 事件就只能绑定到 window 上,而不能绑定到 document 上。

然后再执行 冒泡阶段,然后反着再经过一遍这些节点。

图片

我们会根据事件流经过的顺序,依次执行这些节点上绑定的对应事件函数。

事件委托

假如我有一个好友列表,我希望点击 “聊天” 按钮,拿到对应用户 id,创建并进入到对应用户的聊天会话中。

<ul>
  <li>前端西瓜哥<button>聊天</button></li>
  <li>fe_watermelon<button>聊天</button></li>
  <!-- ... -->
  <li>老王<button>聊天</button></li>
</ul>

最直接的方式是给所有的 button 元素都绑定各自的事件。

节点少的时候还好,如果节点多达上千上万个,就需要声明相当多的事件函数,比较消耗内存。而且 如果列表经常发生动态变更,也会导致大量事件监听的移除和绑定

在这种情况下,事件委托就大有可为了。

事件委托正是利用事件流的冒泡特性,将本来要绑定到多个元素的事件函数,委托到了其祖先元素上

在上面这个例子中,我们可以将事件绑定到 ul 节点上,执行函数时,通过 event 对象拿到必要的信息,进行统一的操作。

document.querySelector('ul').addEventListener('click', (event) => {
  const target = event.target;
  const userId = target.getAttribute('data-user-id');
  if (userId) {
    joinChat(userId);
  }
});

通过 event.target 我们能获得这次事件流的目标节点,然后从该节点对象中提取出需要的信息。

在这里我们需要拿到用户 id,所以需要给 button 元素添加类似 data-user-id 的自定义属性,像这样子:

<ul>
  <li>前端西瓜哥<button data-user-id="5">聊天</button></li>
  <li>fe_watermelon<button data-user-id="99">聊天</button></li>
  <!-- ... -->
  <li>老王<button data-user-id="63">聊天</button></li>
</ul>

这样,不管 li 有多少,更新多频繁,我们只需要维护一个函数就够了。

结尾

事件委托,其实就是将原本应该绑定在子元素的的大量类似的事件监听函数,改为绑定到父元素或祖先元素上,委托祖先元素来处理。

不过在实际开发中,需要用到事件为委托的场景还是比较少,因为我们的列表通常不会太长。

我是前端西瓜哥,持续更新原创前端技术文章,欢迎关注我。

### JavaScript 事件委托的定义、实现及原理解释 #### 一、事件委托的定义 事件委托是一种基于事件冒泡机制的技术,其核心思想是将多个子元素的事件监听器集中绑定到它们共同的父级容器上。这样可以避免为每一个子元素单独绑定事件处理器,从而提升性能并简化代码管理[^3]。 #### 二、事件委托的工作原理 事件委托依赖于 **事件冒泡** 的特性。当一个事件发生在某个 DOM 元素上时,该事件会沿着 DOM 树向上传播至祖先节点。因此,即使目标元素本身未注册特定类型的事件监听器,只要它的上级或顶级父节点绑定了相应的监听函数,仍然可以通过捕获这个传播过来的事件来进行响应[^2]。 例如,在下面的例子中,点击任何一个 `<li>` 子项实际上触发的是附加在其父级 `<ul>` 上面的那个单一 click handler: ```javascript document.getElementById('parentList').addEventListener('click', function(event){ if (event.target && event.target.nodeName === 'LI') { console.log(event.target.textContent); // 打印被点击列表项的内容 } }); ``` 这里的关键点在于判断 `event.target` 是否是我们感兴趣的类型 (`<li>`) 并执行相应动作[^1]。 #### 三、实现方式 以下是通过事件委托来处理一组按钮点击的具体实例演示: 假设有一个无序列表包含若干链接条目,希望每当用户单击其中任意一项时跳转至对应网址地址。传统做法可能是遍历所有 `<a>` 标签逐一挂载 onclick 方法;但借助事件委派模式,则只需要在包裹这些锚点标签的父亲层面上设立统一侦听即可完成相同效果。 ```html <ul id="linkContainer"> <li><a href="#sectionA">Section A</a></li> <li><a href="#sectionB">Section B</a></li> <li><a href="#sectionC">Section C</a></li> </ul> <script type="text/javascript"> const container = document.querySelector('#linkContainer'); container.addEventListener('click', function(e) { const linkElm = e.target.closest('a'); if (!linkElm || !linkElm.matches('a')) return; e.preventDefault(); history.pushState({}, '', linkElm.getAttribute('href')); updateContentBasedOnHash(); // 自定义方法更新页面内容 }, false); </script> ``` 此段脚本首先选取了作为所有超链载体的 ul 容器对象,接着为其添加了一个通用型鼠标按键按下回调闭包。每当检测到来自内部组件发出的通知信号之后,便会进一步确认发送源确实属于预期范围之内(即有效的 a 标记),随后阻止默认行为发生以便自行控制导航流程[^4]。 #### 四、优势总结 - 减少了全局范围内创建过多独立事件句柄所带来的开销; - 动态增删后代节点不影响已存在的监听逻辑有效性; - 维护起来更加方便直观易于扩展升级[^5]。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值