事件委托的本质是利用事件冒泡机制,将监听器绑定在父元素上,通过event.target和closest()识别实际触发事件的子元素,适用于动态DOM、大量同类型子元素及统一事件拦截场景。
JavaScript 事件委托不是一种独立的事件类型,而是把事件监听器绑定在父元素上,依靠 event.target 判断实际触发事件的子元素。它之所以有效,是因为点击、输入等大多数 DOM 事件会从目标元素逐层向上冒泡到 document,中间经过所有祖先节点。
比如给一个动态生成的列表项添加点击逻辑,如果每个 都单独绑定 addEventListener('click', handler),不仅代码冗余,还会因频繁增删节点导致监听器泄漏或重复绑定。
event.target 和 closest() 精准识别目标元素现代写法推荐用 Element.closest(selector) 判断点击是否落在符合规则的子元素上,比手动遍历父节点更安全、语义更清晰:
document.getElementById('list').addEventListener('click', function(e) 
{
const item = e.target.closest('li');
if (item) {
console.log('点击了第', item.dataset.index, '项');
}
});
e.target 是你真正点击的那个元素(可能是 或 ),不一定是
closest('li') 会从 e.target 开始向上查找,包括自己,直到匹配到 或到达边界e.target.tagName === 'LI' —— 它无法处理嵌套结构,比如点击 - 文本
时 e.target 是
不是所有情况都适合事件委托,但以下三类几乎只能靠它解决:
注意:表单控件的 change、input 事件冒泡行为不一致(input 冒泡,change 在某些浏览器中不冒泡到 ),这类事件委托需谨慎测试。
事件委托本身不会让单次点击变快,它的性能收益体现在两个地方:
innerHTML = ... 或 appendChild())无需重新绑定事件,避免反复调用 addEventListener()
closest() 查找,如果选择器过于宽泛(如 closest('*'))或层级过深,可能引入微小延迟真正容易被忽略的是事件对象复用问题:在旧版 IE 中 event 是全局变量,现代浏览器中 e 是局部参数,但若你在异步回调(如 setTimeout)里访问 e.target,可能已失效 —— 此时应提前提取所需属性,如 const id = e.target.id。