JavaScript事件委托

概述

在理解事件委托之前,我们需要先学习事件捕获和事件冒泡。在DOM树结构中,当一个事件发生时,事件是从根节点向叶节点逐级传递的,事件到达目标节点后,又逐级传递到根节点。

  • 事件从根节点传递到叶子节点的过程,称为事件捕获(Event Capture)
  • 事件从叶子节点传递到根节点的过程,称为事件冒泡(Event Bubble)

基本示例

1
2
3
4
5
<body>
<div>
<span>Event Capture and Event Bubble.</span>
</div>
</body>

例如在上面的代码中,点击span时,事件的传递顺序就是:body -> div -> span -> div -> body。其中body -> div -> span即事件捕获,span -> div -> body即事件冒泡。目前处理事件的时机一般都在事件冒泡阶段。

注:先捕捉后冒泡,W3C规定的标准,但是早期浏览器有些相反。

事件委托

事件委托(Event Delegation)是JavaScript中一种很常用的技术。简单地讲,就是当我们需要给一个DOM节点添加事件的时候,我们不直接讲事件绑定到该节点上,而是将事件绑定到某个指定的父节点上去。当目标事件发生时,让业务逻辑在事件冒泡直到指定的父节点时,对比事件目标之后,再去做相关处理。事件委托,就是这样一种处理事件的方法。

为什么要事件委托

也许你会觉得这太麻烦了,我为什么不直接把事件处理放到目标DOM节点上去,而要特地拐弯抹角地去把事件处理放到父节点上去呢。在前端发展迅猛的今天,DOM操作变得越来越频繁和复杂。DOM结构也经常发生动态变化,当频繁添加或者删除DOM节点时,如果也还需要手动去为每一个节点绑定事件,代码量不仅巨大且及其难以维护,这种情况下直接绑定事件到目标节点将是灾难性的。

示例

假定你有DOM结构如下。

1
2
3
4
5
6
7
8
<ul id="parent-list">
<li id="post-1">Item 1</li>
<li id="post-2">Item 2</li>
<li id="post-3">Item 3</li>
<li id="post-4">Item 4</li>
<li id="post-5">Item 5</li>
<li id="post-6">Item 6</li>
</ul>

你需要响应每个LI的点击事件。当UL下的LI不固定,经常新增或者删除时,直接为LI绑定事件显然是不可取的。

使用事件委托的话,我们应该在UL上绑定事件。这里使用Element.matches API来判定是否是目标事件。

1
2
3
4
5
6
7
8
9
// Get the element, add a click listener...
document.getElementById('parent-list').addEventListener('click', function(e){
// e.target was the clicked element
if(e.target && e.target.matches('li#post-4')) {
console.log("post-4 element clicked!");
} else {
console.log('Whatever.');
}
});

一般来说,我们在实际开发应用的时候,很少会直接用写原生的JavaScript实现的事件委托,这里也推荐用各种三方JavaScript库来实现。以Jquery为例,它封装了更多功能,包括了目标节点确认等。

参考

  1. How JavaScript Event Delegation Works - David Walsh