原文: http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/#
传统的事件处理
所谓事件委托即使用单个Event Handler来管理页面上特定的一类事件。这并不是什么新的idea,但对于Web应用的性能而言,是很重要的。比如,有时候,你可能会写如下的代码:
    document.getElementById("help-btn").onclick = function(event){
    openHelp();
};
document.getElementById("save-btn").onclick = function(event){
    saveDocument();
};
document.getElementById("undo-btn").onclick = function(event){
    undoChanges();
}; 
  
  它为页面上每一个可交互的元素提供了一个Event Handler。在一个小型的Web应用中,这可能问题不明显。但如果这是一个大型的网站,存在大量的交互,这么多的handler可能就会出现性能问题了。可以想象,为了处理数以百计的交互,我们需要将大量的DOM元素和时间处理代码绑定起来。这会占用大量的内存,进而导致整个应用运行变慢。而事件委托就可以用来解决这个问题。
    事件冒泡和捕获(Event Bubbling / Capturing)
    
  
  在早期Web开发的年代,浏览器厂商需要考虑一个问题:当用户在Web页面上点击一个区域,如何判断他实际正在进行互交互的元素。与之相关的是我们如何定义交互。在一个元素内点击的操作是有二义性的。如下图所示,如果用户点击了一个button元素,这个交互发生在button内部,同时我们也可以说它发生在body元素的内部,同时还在html元素的内部。
在这个问题被提出来的时候,当时的两个主要浏览器(Netscape Navigator 和 Internet Explorer)采取了不同方式来解决这个问题。Netscape使用了一种叫做事件捕捉的机制,事件首先发生在DOM树上最高层的元素上,然后逐层往下传递到最深的被影响的元素上。所以,在上述例子中,点击事件首先由document元素来处理,然后是html元素,接下来是body,最后才是button元素。
Internet Explorer正好使用了完全相反的方式。他们称之为事件冒泡:最底层的元素应该首先接受到这个时间,然后才是它的父节点,祖父节点……一直到最高层的document元素。值得注意的是,虽然document和<html>元素对应的是页面上同一个可视元素,但它们在DOM树上却是父子关系。Event Bubbling的终点是document元素。
在定义DOM的时候,W3C显然注意到了两种不同方式各自的好处,因此我们现在使用的DOM Level 2事件机制中同时包含了这两种方式。一开始document元素接收到这个事件,进入捕获阶段,将其传递到最底层的元素。在这个元素的处理逻辑完成以后,进入冒泡阶段,将其传递回到document。在DOM Level2事件API中,addEventListener方法接受三个参数:要处理的事件名,事件处理函数,一个bool值(true表示在捕获阶段处理事件,而false表示在冒泡阶段处理事件)。多数Web开发人员经常会被告知使用false作为参数值,以保持和IE中的attachEvent方法相同的行为。比如:
    //bubbling phase handler
document.addEventListener("click", handleClick, false);
//capturing phase handler
document.addEventListener("click", handleClick, true);
  
  
    使用形如 
    
      element.onclick = function(){}
    
     的方式来绑定事件的方式,即DOM Level 1的事件机制,是在冒泡阶段处理事件的(向前兼容性)。大多数浏览器(除了IE)都支持DOM Level 2事件机制,也就是说同时支持事件捕获和冒泡。IE仍然使用的是其专有的,仅支持冒泡方式的事件处理机制。
  
事件委托的关键就是使用事件冒泡的特性在DOM最高层的元素来处理它们(通常是document)。当然,不是所有的事件都支持冒泡(如onsubmit,onfocus,onblur这一类不和输入设备直接相关的时间等),但是鼠标和键盘事件都可以(包括onclick)。而且,很幸运的是,这些通常是你会感兴趣的。重新考虑之前的例子,你可以在document的事件处理函数中,检查事件的目标元素,并完成相应的逻辑。
    document.onclick = function(event){
    //IE doesn't pass in the event object
    event = event || window.event;
    
    //IE uses srcElement as the target
    var target = event.target || event.srcElement;    
    switch(target.id){
        case "help-btn":
            openHelp();
            break;
        case "save-btn":
            saveDocument();
            break;
        case "undo-btn":
            undoChanges();
            break;
        //others?
    }
};
  
  
    事件委托可以让同一事件的处理入口都汇集到一个函数中。所有的click事件现在都会先被单个函数所处理,并根据时间的对象元素委托给合适的函数。同样的,我们把它应用于
    
      mousedown
    
     , 
    
      mouseup
    
     , 
    
      mousemove
    
     , 
    
      mouseover
    
     , 
    
      mouseout
    
     , 
    
      dblclick
    
     , 
    
      keyup
    
     , 
    
      keydown
    
     , 和
    
      keypress
    
     事件. 这里要注意的一点就是,
    
      由于mouseover
    
    
      和mouseout事件的特性(仅当鼠标移出容器时才会触发
    
     mouseout),事件代理对它们来说并不是很实用。
  
当然,你也可以通过事件捕获来完成Event delegation,但这仅在支持事件捕获的浏览器中有效,也就是说IE不支持该方式。
好处
对于一个web应用来说,事件代理具有以下几点好处:
- 较少的事件处理函数.
- 占用更少的内存.
- 减少了Javascript和DOM之间的耦合.
- 
      在改变元素的innerHTML时,不用去移除绑定事件处理函数.
相比于传统的事件处理方式,事件委托提高了大型web应用的总体性能。它对于Javascript库至关重要,如 YUI 和 jQuery 已经使用了这种机制。实现事件委托并不难,但是在用户界面的性能上的提高确实非常显著的。这在你将大量的事件处理函数合并为一个的时候,尤其明显。试试事件委托机制吧!


 
					 
					