事件委托是利用事件冒泡机制将子元素事件监听移至父元素统一处理,通过e.target精准识别目标元素,解决批量绑定、动态节点和逻辑复用问题。

事件委托是把子元素的事件监听逻辑,挪到它们共同的父元素上统一处理,靠的是事件冒泡这个天然机制。
事件委托的核心是事件冒泡
当点击一个子元素(比如列表里的某个 li),事件会先在该元素触发,然后逐级向上传播到它的父元素、祖父元素,直到 document。父元素只要绑定了监听器,就能在冒泡过程中“捕获”这个事件。
关键不是阻止传播,而是利用它——在父元素里通过 e.target 拿到真正被点击的那个子元素,再判断类型、执行对应操作。
为什么必须用 e.target 而不是 this
this 指向的是绑定监听器的父元素;e.target 才是实际被点击的子元素(哪怕它是动态插入的新节点)。
常见误操作:直接对 this 做样式或逻辑处理,结果所有点击都作用在父容器上,而不是目标项。
- 正确写法:if (e.target.matches('button.delete')) { … }
- 推荐用法:e.target.closest('li') 快速定位最近的符合条件的祖先或自身
它解决的三个实际问题
不是为了炫技,而是应对真实场景:
- 页面一次性渲染几百个按钮或列表项,避免创建同等数量的监听器,节省内存
- 列表支持动态增删项(比如无限滚动、搜索过滤),新添加的元素无需重新绑定事件
- 减少重复代码,所有同类交互逻辑集中在一处维护,比如统一记录埋点、加 loading 状态、做权限拦截
一个最小可运行示例
HTML:
<ul id="list"> <li data-id="1">苹果</li> <li data-id="2">香蕉</li> <li data-id="3">橙子</li> </ul>
JS:
document.getElementById('list').addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
const id = e.target.dataset.id;
console.log('点击了编号为', id, '的水果');
}
});
即使后续用 JS 插入新的 li,这段逻辑依然有效。


















