所谓的事件机制,就是用于实现和事件相关的功能的函数,一般我们操作事件需3个功能:
绑定
取消绑定
执行
比如
elem.on('click', fn);
所有js框架都提供事件函数,因为:
浏览器自带的事件不兼容。
目前常用的事件实现有:
[Ext] elem.on elem.un elem.fireEvent (addEventListener 等简写)
[jQuery] elem.bind elem.unbind elem.trigger
[mootools] elem.addEvent elem.removeEvent elem.fireEvent
我使用的是:
elem.on elem.un elem.trigger - 因为最短
(PS,使用 triggerListener 是错误的, 应该 dispatchListener)
jQuery中,将事件名作为函数方便使用。
事件内部如何实现?
也就是说当我们执行 on('click', fn); 做什么事?
最简单也是不可采用的方法是mootools的做法:
对不同的浏览器, IE 使用 attchEvent , 其它使用 addEventListenr
这样感觉是不错,但有2个问题:
当事件有几百个后,效率很低。(不常见)
当 on('click', fA) , on('click', fB)
这时如果click, IE先运行 fB 然后 fA 其它则正常地运行 fA和fB 。(容易造成问题)
要说好的事件机制,当是jQuery的实现。(但效率稍低)
首先,jQuery内部有世界缓存数组。这个数组可用 $().data('event') 返回。这个数据结构为:
event = {
click: [fA, fB]
dblclick: [fC]
};
当浏览器触发 click 后,遍历 event['click'] 全部函数和执行。
我最后使用以下方式实现。
基本和jQuery的一致, 但event数据结构为:
event = {
click: fn_click, // 系统click 后执行 fn_click , fn_click 执行里面的所有函数。
...
}
fn_click = {
handlers: [fA, fB]
target: elem
}
然后是 事件对象的封装。
所谓的事件对象,就是绑定函数的参数 e 。
(熟悉 .net 的同学肯能更喜欢叫它 EventArgs )
因为不同浏览器这个对象不同,所以封装是一定的。
目前全部框架都使用自定义事件对象的方式,把原事件的各值拷到自定义的对象。
但这样有缺点,就是很多值也许用不到,无故计算浪费效率,(事件是常执行的,这效率不可忽略)
Ext则使用函数方式,减小没用的拷贝。如获取屏幕坐标,使用
e.getX() 这有点不美观。但将就了。
我经过多次的考虑,决定不自定义事件对象。直接用自带的,
这样有个缺点: 火狐下无法设置系统 getter 属性。
也就是说:
e.target = e.target.nodeType == 3 ? e.target.parentNode : e.target; // 标准浏览器肯能 target 是文本节点。 IE的 target则是 元素。
在火狐是错的。 因为 target是只读的。
接下来是模拟事件触发,
在 jQuery 中,很辛苦的用代码模拟了支持冒泡的事件。 虽然在IE有完美的 fireEvent ,标准浏览器也有 dispatchListener 函数。
不过如果自己模拟,可以避免浏览器不同,实现统一。