事件代理

事件阶段

当一个DOM事件被触发的时候,他并不是只在它的起源对象上触发一次,而是会经历三个不同的阶段。简而言之:事件一开始从文档的根节点流向目标对象(捕获阶段),然后在目标对向上被触发(目标阶段),之后再回溯到文档的根节点(冒泡阶段)。

事件捕获阶段(Capture Phase)

事件的第一个阶段是捕获阶段。事件从文档的根节点出发,随着DOM树的结构向事件的目标节点流去。途中经过各个层次的DOM节点,并在各节点上触发捕获事件,直到到达时间的目标节点。捕获阶段的主要任务是简历传播路径,在冒泡阶段,时间会通过这个路径回溯到文档根节点。

目标阶段(Target Phase)

当事件到达目标节点时,事件就进入了目标阶段。事件在目标节点上被触发,然后逆向回流,直到传播到最外层的文档节点。
对于多层嵌套的节点,鼠标和指针事件经常会被定位到最里层的元素上。假设,你在一个div元素上设置了click的监听函数,而用户点击在了这个div元素内部的p元素上,那么p元素就是这个时间的目标元素。事件冒泡让我们可以在这个div或者更上层的元素上监听click事件,并且时间传播过程中触发回调函数。

Event对象提供了一个属性叫target,可以返回事件的目标节点。标准浏览器用event.target,IE用event.srcElement

兼容IE写法:

window.onload = function(){   
  var ul = document.getElementById("list"); 
  ul.onclick = function(e){     
    var e = e || window.event;    
    var target = e.target || e.srcElement;     
    if(target.nodeName.toLowerCase() == 'li'){          
      //do something   
    }  
  } 
}

事件冒泡阶段(Bubble Phase)

事件在目标事件上触发后,并不在这个元素上终止。它会随着DOM树一层层向上冒泡,直到到达最外层的根节点。也就是说,同一事件会一次在目标节点的父节点,父节点的父节点,直到最外层的节点上触发。

注:绝大多数事件是会冒泡的,但并非所有的!
焦点事件 focus,blur 和鼠标事件 mouseleave 不会冒泡

事件传播的最上层对象是windows,接着是document,html和body直到目标节点。可以通过 Event.eventPhase 来判断事件流当前处于哪一个阶段,如下:

  1. eventPhase 为 0: 这个时间没有事件正在被处理
  2. eventPhase 为 1: 捕获阶段
  3. eventPhase 为 2: 目标阶段
  4. eventPhase 为 3: 冒泡阶段

事件代理(委托)

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。通俗地讲,就是把一个元素相应事件(click、keydown、keypress等等)的函数委托到另一个元素上。一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

委托的优点

(1).减少内存消耗

如果有一个列表,列表之中有大量的列表项,需要在点击列表项的时候相应一个事件。如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能。因此比较好的办法就是把这个事件绑定到其父元素‘ul’上,然后在执行事件的时候再去匹配判断目标元素。

<ul id="list">
  <li>1</li>
  <li>2</li>
  ...
  <li>n</li>
</ul>

(2).动态绑定事件

很多情况需要通过动态操作来增加或者去除列表项元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件。如果使用事件委托就和目标元素的增减没有关系,因为它是绑定在父层元素上的,所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。

关于Jquery中的事件委托

Jquery中的事件委托主要有以下三种:

  1. $.fn.on(events [, selector ] [, data ], handler)
  2. $.fn.delegate(selector, eventType, handler) (使用$.fn.undelegate解除事件绑定)
  3. $.fn.live(events, handler) (使用$.fn.die解除事件绑定)

注:从jQuery 1.3开始提供了$.fn.live方法,在1.3版本这个方法只支持特定的事件类型。自从jQuery 1.4.2开始提供$.fn.delegate方法,应优先选择这个方法。$.fn.live方法在jQuery 1.9中已经移除