我们习惯理所当然的使用jQuery提供的高级事件API,jQuery帮我们处理了事件注册机制在各个浏览器间的兼容性,帮我们实现了高级的事件委托机制,大大的提高了我们的工作效率;然而我从来都不是一个知其然不知其所以然的工程师,你是否一样?如果你同样对其实现细节感兴趣,请仔细阅读本文。本文不涉及jQuery提供的自定义事件。
我将本文组织为如下结构:
1.使用原生API有什么问题
2.大牛是如何解决的
3.jQuery的实现
1. 使用原生API有什么问题
(1) 最原始的方法是直接对dom元素的onclick、onmouseover等属性赋值一个函数:
然而这种方式一次只能添加一个事件处理函数,不具有通用性。
(2) 对于符合最新W3C标准的浏览器(IE>8,chrome,firefox,android)可以使用element.addEventListener,将事件类型作为一个参数传递。
element.addEventListener是支持添加多个事件的,调用顺序是事件添加顺序。并且我们可以通过传递第三个参数来控制事件的触发时机是捕获阶段还是冒泡阶段,默认为冒泡阶段。然而低版本的IE(<=8)不支持element.addEventListener。
(3)对于低版本的IE(<=8)相应的提供了element.attachEvent:
element.attachEvent的事件类型参数格式必须是on+事件类型,但是监听函数内部的this指针指向的却不是触发事件的element。
由此可见,以上三种方法直接使用原生API的方法都是有问题的或者说不完美的。
2.Dean Edwards大牛的实现
Dean Edwards在2005年写的了addevent的库,用于处理对浏览器的兼容性问题。理解了这个addEvent库,我们可以更好的理解jQuery的事件系统。
Dean Edwards的这段代码还是比较好理解的,他首先将事件组织成一个如下结构,即每一种事件类型维持一个绑定事件处理函数列表,当某一类事件触发时,通过一个wrapper函数(通过onEvent API绑定的handleEvent)轮询对应列表并依次处理。事件列表结构组织如下:
如果理解了以上代码,那么jQuery的事件处理系统就容易懂了。
3. jQuery的实现
其实用Dean Edwards的addEvent库就可以很好的实现了事件处理程序的健壮性:
(1).跨浏览器兼容性
(2).this变量的正确指向
(3).支持添加多个事件处理函数
但是jQuery的真正强大之处在于它对事件委托的实现,这也是我们接下来分析的要点,关于事件委托的介绍请参考我的另一篇文章。
对于事件绑定,jQuery提供了一个万能函数on,$(selector).on(event,childSelector,data,function,map)
,基本上jQuery的各种事件函数变体都是对on函数的封装:
比如:$(selector).click(),
$(selector).bind ()
还有$(selector).live()
,$(selector).delegate()
,具体各个api的区别请参考jQuery文档。
(1) 解析on函数
on函数内部调用了jQuery.event.add
的add函数。
$(selector).on(event,childSelector,data,function,map)
||
\||/
\/
jQuery.event.add( this, types, fn, data, selector );
我们来分析以下add函数的代码:
on函数主要建立事件元数据,并将元数据放到对应的列表里。最终生成如下事件数据结构:
我们也看到了,on函数通过绑定一个wrapper函数来调用真实的事件处理函数,类似大牛Dean Edwards的实现。而wrapper函数中调用了dispatch函数,下面我们分析一下dispatch。
(2) 解析dispatch函数
dispatch函数首先通过handlers函数对事件元数据队列进行预处理:委托元数据生成以及事件最终排序
jQuery.event.dispatch.apply( elem, arguments )
||
\||/
\/
jQuery.event.handlers.call( this, event, handlers )
我们先看一下handlers函数的详细解析,handler函数主要是实现了委托的功能。首先从事件触发节点开始到事件委托节点结束,模拟冒泡过程,在冒泡的过程中遍历elemData中的委托事件元数据,选择匹配的节点,生成handlerQueue以供dispatch使用。其中handlerQueue的结果类似:[{elem: node ..., handlers: this},...,{elem: node ..., handlers: list}]
。
理解了handlers函数的实现,dispatch函数的逻辑就简单了:
没错,现在我们基本分析完了jQuery事件系统的实现。可以说jQuery的实现参考了Dean Edwards的代码,同时添加了事件委托的实现。
本文我需要参考了如下博客
http://www.cnblogs.com/lidabo/archive/2012/04/01/2429128.html
http://www.cnblogs.com/wangfupeng1988/p/3659470.html