先讲讲DOM事件流:DOM2级事件 规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段;在DOM事件流中,实际的目标在捕获阶段不会收到事件,紧接着是目标阶段事件发生,并在事件处理中被看成冒泡阶段的一部分,最后冒泡阶段发生,事件又传播回文档;
事件阶段:
当一个DOM事件被触发的时候,他并不是只在它的起源对象上触发一次,而是会经历三个不同的阶段。简而言之:事件一开始从文档的根节点流向目标对象(捕获阶段),然后在目标对向上被触发(目标阶段),之后再回溯到文档的根节点(冒泡阶段)如图所示
事件委托(event delegation)
对“事件处理程序过多”问题的解决方案是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
避免对特定的每个节点添加事件监听,相反,事件监听器是被添加到他们的父元素上,事件监听器会分析从子元素冒泡上来的事件,找到是哪个字元素的事件。
如下例子,每个单独的<li>元素被点击时有各自不同的事件发生,如果给它们分别单独绑定事件,但这些<li>元素有可能会被删除也有可能会新增系元素,那么监听将会变得很棘手;
例子:
<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》时可以检查到对象的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!");
}
});
我们先给父元素添加事件监听器,当有事件触发监听器时检查事件的来源,如果是一个li元素则找到了目标,如果不是则忽略该事件。
// 获得父元素DIV, 添加监听器... document.getElementById("myDiv").addEventListener("click",function(e) { // e.target是被点击的元素 if(e.target && e.target.nodeName == "A") { // 获得CSS类名 var classes = e.target.className.split(" "); // 搜索匹配! if(classes) { // For every CSS class the element has... for(var x = 0; x < classes.length; x++) { // If it has the CSS class we want... if(classes[x] == "classA") { // Bingo! console.log("Anchor element clicked!"); // Now do something here.... } } } } });
JavaScript事件代理和委托以及事件的三个阶段详细可以查看:https://segmentfault.com/a/1190000002613617