- <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 的相关信息并飘出悬浮窗以显示详细信息,或者当某个 Li 被点击的时候需要触发相应的处理事件。我们通常的写法,是为每个 Li 都添加一些类似 onMouseOver 或者 onClick 之类的事件监听。
- function addListeners4Li(liNode) {
- liNode.onclick = function clickHandler() {...
- };
- liNode.onmouseover = function mouseOverHandler() {...
- }
- }
- window.onload = function() {
- var ulNode = document.getElementById("parent-list");
- var liNodes = ulNode.getElementByTagName("Li");
- for (var i = 0,
- l = liNodes.length; i < l; i++) {
- addListeners4Li(liNodes[i]);
- }
- }
如果这个 UL 中的 Li 子元素会频繁地添加或者删除,我们就需要在每次添加 Li 的时候都调用这个 addListeners4Li 方法来为每个 Li 节点添加事件处理函数。这就添加的复杂度和出错的可能性。
更简单的方法是使用事件代理机制,当事件被抛到更上层的父节点的时候,我们通过检查事件的目标对象(target)来判断并获取事件源 Li。下面的代码可以完成我们想要的效果:
- // 获取父节点,并为它添加一个click事件
- document.getElementById("parent-list").addEventListener("click",
- function(e) {
- // 检查事件源e.targe是否为Li
- if (e.target && e.target.nodeName.toUpperCase == "LI") {
- // 真正的处理过程在这里
- console.log("List item ", e.target.id.replace("post-"), " was clicked!");
- }
- });
为父节点添加一个 click 事件,当子节点被点击的时候,click 事件会从子节点开始向上冒泡。父节点捕获到事件之后,通过判断 e.target.nodeName 来判断是否为我们需要处理的节点。并且通过 e.target 拿到了被点击的 Li 节点。从而可以获取到相应的信息,并作处理。
之前的介绍中已经说到了浏览器的事件冒泡机制。这里再详细介绍一下浏览器处理 DOM 事件的过程。对于事件的捕获和处理,不同的浏览器厂商有不同的处理机制,这里我们主要介绍 W3C 对 DOM2.0 定义的标准事件。
DOM2.0 模型将事件处理流程分为三个阶段:一、事件捕获阶段,二、事件目标阶段,三、事件起泡阶段。如图:
事件捕获:当某个元素触发某个事件(如 onclick),顶层对象 document 就会发出一个事件流,随着 DOM 树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。
事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
事件起泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。如果想阻止事件起泡,可以使用 e.stopPropagation()(Firefox)或者 e.cancelBubble=true(IE)来组织事件的冒泡传播。
下面看一下 Dojo 和 jQuery 中提供的事件代理接口的使用方法。
首先是 jQuery:
- $("#link-list").delegate("a", "click",
- function() {
- // "$(this)" is the node that was clicked
- console.log("you clicked a link!", $(this));
- });
jQuery 的 delegate 的方法需要三个参数,一个选择器,一个时间名称,和事件处理函数。
而 Dojo 的与 jQuery 相似,仅是两者的编程风格上的差别:
- require(["dojo/query", "dojox/NodeList/delegate"],
- function(query, delegate) {
- query("#link-list").delegate("a", "onclick",
- function(event) {
- // "this.node" is the node that was clicked
- console.log("you clicked a link!", this);
- });
- })
Dojo 的 delegate 模块在 dojox.NodeList 中,提供的接口与 jQuery 一样,参数也相同。
通过上面的介绍,大家应该能够体会到使用事件委托对于 web 应用程序带来的几个优点:
1. 管理的函数变少了。不需要为每个元素都添加监听函数。对于同一个父节点下面类似的子元素,可以通过委托给父元素的监听函数来处理事件。
2. 可以方便地动态添加和修改元素,不需要因为元素的改动而修改事件绑定。
3.JavaScript 和 DOM 节点之间的关联变少了,这样也就减少了因循环引用而带来的内存泄漏发生的概率。
写到这里,突然想起了之前对于 Dojo DataGrid 的困惑:那么多的 rows 和 cells,如何处理他们事件之间的关系。现在想想,使用委托就很简单了。所有的事件委托到 grid 最外层的节点上,当事件发生的时候通过一些方法来获取和添加事件的额外属性,如 rowIndex, cellIndex,之后在分配到 onRowClick,onCellClick 之类的处理函数上。
来源: http://www.bubuko.com/infodetail-2085781.html