[译]Javascript中的事件代理是怎么工作的

javascript中有个很火的术语叫事件代理。这玩意儿的好处就是可以把事件监听挂在父元素上,而不是直接挂载在对应的节点上。在父元素的监听事件里可以通过事件冒泡找到对应的子节点,然后做你想对子节点要做的事。这个概念挺简单的,但很多人不知道它到底是怎么工作的,还是让寡人来show点展示基本事件的代码来解释给你听吧。

首先我们有一个父元素ul,它下面有些子元素:

<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上分别添加一个事件监听,但是如果里面的li会反复的被添加或者删除,那你的监听事件也跟着反复的添加和删除的话,这是很可怕的事情,尤其如果你添加和删除的代码不在一个地方的时候。更好的方法就是在父元素ul上添加一个事件监听。但是,事件加在父元素上,点击li的时候,我怎么知道是哪个元素被点击了呢?

这个简单, 当事件冒泡到ul的时候,你在监听事件函数里检查event对象的target属性,该属性表示这次点击事件实际点到的对象。比如下面这个代码片段:

// 查找到元素,然后添加点击事件监听器
document.getElementById("parent-list").addEventListener("click", function(e) {  
    // e.target 即被点击的对象
    // 如果它存在并且它是个LI的话
    if(e.target && e.target.nodeName == "LI") {
        // 找到了它,打印它的ID
        console.log("List item ", e.target.id.replace("post-"), " was clicked!");
    }
});

代码中,先给父元素添加一个click的事件监听器,当事件被触发,检查事件的对象元素的类型是否是我们要找的li,最后面找到了就执行指定的代码,如果没找到,这次事件即被忽略。这个例子非常简单——ulli一目了然。下面举个复杂点的例子。假如有个DIV,它下面有各种子元素,但我们只想响应有class=classAA标签上的事件:

// 找到父级DIV,添加监听事件...
document.getElementById("myDiv").addEventListener("click",function(e) {  
    // 判断e.target元素是不是A标签
    if(e.target && e.target.nodeName == "A") {
        // 拿到它的class,转成数组
        var classes = e.target.className.split(" ");
        if(classes) {
            // 遍历数组
            for(var x = 0; x < classes.length; x++) {
                // 如果有我们要找的样式
                if(classes[x] == "classA") {
                    // 找到了!
                    console.log("Anchor element clicked!");
                    // 做你想做的事....
                }
            }
        }

    }
});

上面这个例子中,我们不仅要匹配标签还要匹配样式名,稍微有点复杂,但还挺容易看明白它的意思。另外,我们假设A里面有个SPAN标签,那SPAN标签就是e.target元素,那就还要沿着这个SPAN往下找,直到找到含有classA样式的A元素。

现在大部分开发者都在用javascript库来作dom操作和事件处理,我建议用库提供的方法来做事件代理,因为它们能提供更复杂的事件代理和元素查找。

希望这篇文章能够让你清晰的理解事件代理的概念和机制,方便你更好的利用它!

原文链接:How JavaScript Event Delegation Works: https://davidwalsh.name/event-delegate