由于事件处理程序可以为现代Web应用程序提供交互能提供交互能力,因此许多开发人员会不分青红皂白地向页面中添加 大量的处理程序。当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委托给父节点来触发处理函数。但是,如果添加到页面上的事件处理程序数量一大会影响到页面的整体运行性能。原因在于:
每个函数都是对象,都会占用内存,内存中的对象越多,性能就会越差;必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟到整个页面的交互就绪时间。 那么,“事件处理程序过多”问题的解决方案就是事件委托。如果你的应用程序最终可能有几百个事件监听器,更高效的解决方案是将一个事件侦听器实际绑定到整个容器上,然后在实际单击时可以访问每个确切元素。这被称为事件委托,比为每个元素单独绑定事件处理程序更高效。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。 假设有一个 ul的父节点,包含了很多个 li的子节点: <ul id="mylinks"> <li id="goSomewhere">Go somewhere</li> <li id="doSomething">Do something</li> <li id="sayHi">Say hi</li> </ul> 当我们的鼠标移到li上的时候,需要获取此Li的相关信息并飘出悬浮窗以显示详细信息,或者当某个li被点击的时候需要触发相应的处理事件。我们通常的写法,是为每个li都添加一些onClick之类的事件监听,这里为了兼容不同的浏览器,所以添加了一个兼容不同浏览器的代码。 var EventUtil = { addHanlder: function (element, type , hanlder) { if(element.addEventListener){ element.addEventListener(type, hanlder, false); }else if(element.attachEvent){ element.attachEvent("on" + type, hanlder); }else{ element["on" + type] = hanlder; } }, removeHanlder: function (element, type ,hanlder) { if(element.removeEventListener){ element.removeEventListener(type, hanlder, false); }else if(element.detachEvent){ element.detachEvent("on" + type, hanlder); }else{ element["on" + type] = null; } } }; var item1 = document.getElementById("goSomewhere"); var item2 = document.getElementById("doSomething"); var item3 = document.getElementById("sayHi"); EventUtil.addHanlder(item1,"click", function () { location.href="http://baidu.com"; }); EventUtil.addHanlder(item2,"click", function () { document.title="I changed the document's title"; }); EventUtil.addHanlder(item3,"click", function () { console.log("Hi!"); }); 如果在一个复杂的Web应用程序中,对所有可点击的元素都采用这种方式,那么结果就会数不清的代码用于添加事件处理程序。此时,我们可以采用事件委托技术来解决这个问题。在使用事件委托时,我们只需要在DOM树中尽量最高的层次上添加一个事件处理程序,如我们可以将上面的例子修改为: var EventUtil = { addHanlder: function (element, type , hanlder) { if(element.addEventListener){ element.addEventListener(type, hanlder, false); }else if(element.attachEvent){ element.attachEvent("on" + type, hanlder); }else{ element["on" + type] = hanlder; } }, removeHanlder: function (element, type ,hanlder) { if(element.removeEventListener){ element.removeEventListener(type, hanlder, false); }else if(element.detachEvent){ element.detachEvent("on" + type, hanlder); }else{ element["on" + type] = null; } }, getEvent:function(e) { e = e || window.event; return e; }, getTarget:function(e) { return e.target || e.srcElement; } }; var list = document.getElementById("myLinks"); EventUtil.addHanlder(list,"click", function (event) { var event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); switch(target.id){ case "goSomewhere": location.href="http://baidu.com"; break; case "doSomething": document.title="I changed the document's title"; break; case "sayHi": console.log("Hi!"); break; } }); 总结: 通过上面的介绍,大家应该能够体会到使用事件委托对于web应用程序带来的几个优点: 可以大量节省内存占用,减少事件注册。可以方便地动态添加和修改元素,不需要因为元素的改动而修改事件绑定。JavaScript和DOM节点之间的关联变少了,这样也就减少了因循环引用而带来的内存泄漏发生的概率。 缺点: 不是所有的事件都能冒泡的。blur、focus、load和unload不能像其它事件一样冒泡。事实上blur和focus可以用事件捕获而非事件冒泡的方法获得(在IE之外的其它浏览器中)。在管理鼠标事件的时候有些需要注意的地方。如果你的代码处理mousemove事件的话你遇上性能瓶颈的风险可就大了,因为mousemove事件触发非常频繁。而mouseout则因为其怪异的表现而变得很难用事件代理来管理。 最后写一个完整的例子,需求是点击li的时候,获取li的标签内容。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul id="myUl"> <li>list1</li> <li>list2</li> <li>list3</li> </ul> <script> //点击li,弹出li中的内容 window.onload = function () { var EventUtil = { addHander: function (element, type, hander) { if (element.addEventListener) { element.addEventListener(type, hander, false); } else if (element.attachEvent) { element.attachEvent("on" + type, hander); } else { element["on" + type] = hanlder; } } }; var oUl = document.getElementById('myUl'); EventUtil.addHander(oUl, "click", function (event) { var event = event || window.event; var target = event.target; if (target.nodeName.toLowerCase() === 'li') { console.log(target.innerHTML); } }); } </script> </body> </html>