Skip to content

目录

1. 核心原理:事件冒泡

当你点击一个按钮时,你不仅仅是点击了那个按钮。在浏览器看来,你同时还点击了按钮所在的容器、整个页面、甚至 window。

事件冒泡 指的是,当一个元素上的事件被触发时,该事件会从这个元素开始,逐级向上层父元素传播,直到 document 对象。

  • 比喻: 就像往水里扔一块石头(触发事件的元素),涟漪(事件)会从中心点不断向外扩散(向父元素传播)。

2. 什么是事件委托?

基于事件冒泡的原理,我们不必为每个子元素都绑定事件监听器,而是只在它们的父元素上绑定一个监听器。这个父元素就像一个"受委托"的代表,统一处理所有子元素上冒泡过来的事件。

场景对比

假设我们有一个商品列表 <ul>,里面有很多 <li> 项,我们想在点击每个 <li> 时获取其内容。

  • 常规做法 (不推荐):

    javascript
    const items = document.querySelectorAll('ul li')
    items.forEach(item => {
      item.addEventListener('click', () => {
        console.log('你点击了:', item.textContent)
      })
    })
    • 缺点:
      1. 性能开销大: 如果有 1000 个 <li>,就需要创建 1000 个事件监听器,占用大量内存。
      2. 维护困难: 如果通过 JS 动态添加了一个新的 <li>,你需要手动为这个新元素再次绑定事件。
  • 事件委托 (推荐):

    javascript
    const list = document.querySelector('ul')
    list.addEventListener('click', event => {
      // 只有当被点击的确实是 li 元素时才响应
      if (event.target.tagName === 'LI') {
        console.log('你点击了:', event.target.textContent)
      }
    })
    • 优点:
      1. 性能好: 无论有多少子元素,都只需要在父元素上绑定一个监听器。
      2. 维护方便: 动态添加的子元素也能被监听到,因为事件会自然冒泡到父元素,而父元素的监听器一直都在。

3. event.target vs event.currentTarget:关键区别

在处理委托事件时,正确区分 event.targetevent.currentTarget至关重要。

属性描述在委托场景中比喻
event.target事件的真正来源。它是用户实际点击的那个最具体的元素。可能是 <li>,也可能是 <li> 里的 <span><img>包裹的目标:快递员要去送一个包裹,target 就是包裹上写的具体门牌号,例如"302室"。
event.currentTarget监听器所在的元素。也就是你调用 addEventListener 的那个元素。永远是父元素 <ul>送达的大楼:快递员把车停在哪栋大楼下,这栋大楼就是 currentTarget,例如"幸福小区A栋"。

总结: 在事件委托的回调函数中,我们通常关心的是 event.target,因为它告诉我们是哪个子元素触发了事件。而 event.currentTarget 则始终是那个我们绑定事件的父元素。

基于 VitePress 的本地知识库