jQuery 为 dom 处理而生,那么处理兼容的手段自然是独树一帜了,所以:
jQuery对事件的对象的兼容问题单独抽象出一个 fix 类,用来重写这个事件对象
jQuery 利用 jQuery.event.fix()
来解决跨浏览器的兼容性问题,统一接口。
除该核心方法外,我们要根据事件的类型,统一接口的获取,所以 jQuery 引入了 (jQuery.event) props、 fixHooks、keyHooks、mouseHooks 等数据模块。
最后 fixHooks 对象用于缓存不同事件所属的事件类别,比如:
fixHooks['click'] === jQuery.event.mouseHooks; fixHooks['keydown'] === jQuery.event.keyHooks; fixHooks['focusin'] === {};
从源码处获取对事件对象的操作,通过调用 jQuery.Event 重写事件对象,将浏览器原生 Event 的属性赋值到新创建的 jQuery.Event 对象中去。
event = new jQuery.Event( originalEvent );
event 就是对原生事件对象的一个重写了,为什么要这样,jQuery要增加自己的处理机制呗,这样更灵活,而且还可以传递 data 数据,也就是用户自定义的数据。
构造出来的新对象:
看图,通过 jQuery.Event 构造器,仅仅只有一些定义的属性与方法,但是原生的事件对象的属性是不是丢了?
所以还需要把原生的的属性给混入到这个新对象上,那么此时带来一个问题,不同事件会产生了不同的事件对象,拥有不同的属性,所以还有一套适配的机制,根据不同的触发点去适配需要混入的属性名。
扩展通过 jQuery.Event 构造出的新事件对象属性。
//扩展事件属性 this.fixHooks[ type ] = fixHook = rmouseEvent.test( type ) ? this.mouseHooks : rkeyEvent.test( type ) ? this.keyHooks : {};
有一些属性是共用的,都存在,所以单独拿出来就好了。
props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
然后把私有的与公共的拼接一下。
copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
然后混入到这个新的对象上。
jQuery 自己写了一个基于 native event 的 Event 对象,并且把 copy 数组中对应的属性从 native event 中复制到自己的 Event 对象中。
while ( i-- ) { prop = copy[ i ]; event[ prop ] = originalEvent[ prop ]; }
在最后 jQuery 还不忘放一个钩子,调用 fixHook.fitler 方法用以纠正一些特定的 event 属性。例如 mouse event 中的 pageX,pageY,keyboard event中的 which,进一步修正事件对象属性的兼容问题。
fixHook.filter? fixHook.filter( event, originalEvent ) : event
fixHook 就是在上一章,预处理的时候用到的,分解 type 存进去的,针对这个特性的单独处理,最后返回这个“全新的”Event 对象。
总的来说 jQuery.event.fix 做的事情:
1.将原生的事件对象 event 修正为一个新的可写 event 对象,并对该 event 的属性以及方法统一接口 2.该方法在内部调用了 jQuery.Event(event) 构造函数
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script src="http://www.imooc.com/static/lib/jquery/1.9.1/jquery.js" type="text/javascript"></script> <title>重写事件对象</title> </head> <body> <div id="aaron"> <div> <p>点击重写事件对象</p> </div> </div> <ul></ul> <script type="text/javascript"> function returnTrue() { return true; } function returnFalse() { return false; } //模拟出事件对象 //增加调用判断方法 var Event = function(src, props) { if (src && src.type) { this.originalEvent = src; this.type = src.type; this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && src.returnValue === false ? returnTrue : returnFalse; } else { this.type = src; } this.timeStamp = src && src.timeStamp || jQuery.now(); this[jQuery.expando] = true; }; Event.prototype = { isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if (e && e.preventDefault) { e.preventDefault(); } }, stopPropagation: function() { var e = this.originalEvent; this.isPropagationStopped = returnTrue; if (e && e.stopPropagation) { e.stopPropagation(); } }, stopImmediatePropagation: function() { var e = this.originalEvent; this.isImmediatePropagationStopped = returnTrue; if (e && e.stopImmediatePropagation) { e.stopImmediatePropagation(); } this.stopPropagation(); } }; function fix(event) { //原始事件引用 var originalEvent = event; //鼠标事件对象的属性 var props = "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "); //创建事件对象 var event = new Event(originalEvent); //混入事件属性 i = props.length; while (i--) { prop = props[i]; event[prop] = originalEvent[prop]; } return event } document.getElementById('aaron').addEventListener('click',function(e){ e = fix(e) for(var i in e){ show('属性'+i+' 值:' +e[i]) } },false) function show(data) { $('ul').append('<li>' + data + '</li>') } </script> </body> </html>