jquery源码分析(七)——事件模块 event(二)

上一章节探讨了事件的一些概念,接下来看下jQuery的事件模块。

 

jQuery对事件的绑定分别有几个API:.bind()/.live()/.delegate()/.on()/click(),

      不管是用什么方式绑定,归根到底还是用addEventListener/attachEvent(IE)处理的,正如jQuery的选择器一样不管如何匹配最终还是使用浏览器提供的几个接口处理。

那么现在就有个疑问,事件为什么还要区分那么多不同的处理方案?

      这里就要涉及到之前提到的 DOM 事件处理模型了,捕获与冒泡传统的事件处理给某一元素绑定了一个点击事件,传入一个回调句柄处理。element.addEventListener('click',doSomething,false),对比传统事件来存在这些问题

  第一:大量的事件绑定,性能消耗,而且还需要解绑(IE会泄漏);

  第二:绑定的元素必须要存在;

  第三: 后期生成HTML会没有事件绑定,需要重新绑定

  第四: 语法过于繁杂

针对这些问题,因此jQuery引进是采用委托的机制思想来处理。

     谈到事件委托来复习下上节提到的事件模型。DOM 有个事件流的特性,也就是说我们在页面上触发节点的时候事件都会上下或者向上传播,称为事件捕捉和事件冒泡。,DOM2.0 模型将事件处理流程分为三个阶段:事件捕获阶段、事件目标阶段、事件起泡阶段

                                               

事件传送可以分为3个阶段。
(1)在事件捕捉(Capturing)阶段,事件将沿着DOM树向下转送,目标节点的每一个祖先节点,直至目标节点。例如,若用户单击了一个超链接,则该单击事件将从document节点转送到html元素,body元素以及包含该链接的p元素。在此过程中,浏览器都会检测针对该事件的捕捉事件监听器,并且运行这件事件监听器。
(2)在目标(target)阶段,浏览器在查找到已经指定给目标事件的事件监听器之后,就会运行该事件监听器。目标节点就是触发事件的 DOM 节点。例如,如果用户单击一个超链接,那么该链接就是目标节点(此时的目标节点实际上是超链接内的文本节点)。
(3)在冒泡(Bubbling)阶段,事件将沿着DOM树向上转送,再次逐个访问目标元素的祖先节点到document节点。该过程中的每一步。浏览器都将检测那些不是捕捉事件监听器的事件监听器,并执行它们。

换句话说,事件委托就是事件目标自身不处理事件,而是把处理任务委托给其父元素或者祖先元素,甚至根元素(document),如.live()

      对于jQuery中提供多个api来绑定事件:不管你用的是(click / bind / delegate)之中哪个方法,最终都是 jQuery 底层都是调用 on 方法来完成最终的事件绑定。因此从某种角度来讲除了在书写的方便程度及习惯上挑选,不如直接都采用 on 方法来的痛快和直接使用.on()方法事件处理程序到当前选定的 jQuery 对象中的元素。

  在jQuery 1.11中,.on()方法提供绑定事件处理的所有功能、效果不言而喻了,除了性能的差异,通过委托的事件还能很友好的支持动态绑定,只要 on 的delegate 象是 HTML 页面原有的元素,由于是事件的触发是通过Javascript的事件冒泡机制来监测,所以对于所有子元素(包括后期通过JS生成的元素)所有的事件监听均能有效,且由于不用对多个元素进行事件绑定,能够有效的节省内存的损耗。

 

说了这么多,jQuery提供了这么多绑定的方法,具体有什么区别我们来了解一下:

(1)、.bind()方法:用于直接附加一个事件处理程序到元素上,处理程序附加到 jQuery 对象中当前选中的元素,所以在 .bind() 绑定事件的时候这些元素必须已经存在,很明显就是直接调用没利用委托机制。

(2)、live()方法:将委托的事件处理程序附加到一个页面的 document 元素,从而简化了在页面上动态添加的内容上事件处理的使用。这和delegate() 方法类似,只是把所有事件都委托到document对象上来处理,增加冒泡处理时间。现在已经摒弃该方法了,不推荐使用。还是用例子说明一下:

    $('a').live('click', function() { alert("!")})

jQuery 把 alert 函数绑定到 $(document) 元素上,并使用 ’click’和 ’a’作为参数。任何时候只要有事件冒泡到 document 节点上,它就查看该事件是否是一个 click 事件,以及该事件的目标元素与’a’这一CSS 选择器是否匹配,如果都是的话,则执行函数。由于live缺点太多,这里就不一一赘述了。

 

(3)、 delegate(): 为了突破单一 .bind() 方法的局限性,实现事件委托,引入了.live()方法。后来,为解决“事件传播链”过长的问题,新版本又支持为 .live() 方法指定上下文对象。而为了解决无谓生成元素集合的问题,之后版本干脆直接引入了一个新方法 .delegate()。

使用 .delegate(),前面的例子可以这样写:

  $('#element).delegate('a', 'click', function() { alert("!!!") });

jQuery 查找(‘#element’),并将click 事件和’a’这一CSS选择器作为参数把 alert 函数绑定到(‘#element)上。

       事件监听原理是:任何时候只要有事件冒泡到$(‘#element)上,它就查看该事件事件类型是否是click事件,以及该事件的目标元素(curTarget)是否与CCS选择器相匹配。如果都满足就执行函数。可以注意到,这一过程与.live()类似,但是其把处理程序绑定到具体的元素而非document这一根上,从而大大减少事件传播过程(事件冒泡过程)。

       由此可见.delegate() 方法是一个相对完美的解决方案。但在DOM结构简单的情况下,也可以使用.live()。

(4)、on方法:其实 .bind(), .live(), .delegate()都是通过.on()来实现的,.unbind(), .die(), .undelegate()也是一样的都是通过.off()来实现的,该接口只是提供了一种统一绑定事件的方法


总得来说,虽然bind/delegate/live这三个方法都能实现DOM节点事件绑定,但是可以用 .on() 来代替上述的 3 种方法

说了这么多,具体使用过程中该用那个方法呢?

下面来总结一下:

1. 为DOM中的很多元素绑定相同事件;
2. 为DOM中尚不存在的元素绑定事件;
3. 用.bind()的代价是非常大的,它会把相同的一个事件处理程序hook到所有匹配的DOM元素上
4. 不要再用.live()了,它已经不再被推荐了,而且还有许多问题
5. .delegate()会提供很好的方法来提高效率,同时我们可以添加一事件处理方法到动态添加的元素上

实际运用中,事件委托机制(基于事件冒泡)仍存在不足之处:
1. 并非所有的事件都能冒泡,如load, change, submit, focus, blur
2. 加大管理复杂
3. 不好模拟用户触发事件
4. 如何取舍就看项目实际中运用了。
 
posted @ 2016-08-24 15:54  hoboStage  阅读(226)  评论(0编辑  收藏  举报