上一篇正式推出了我的事件模块event-v0.1,已经搭起了它的初始框架。或许有人要说,与众多JS库或框架相比,它还没有解决事件对象的兼容性问题。是的,我故意将此放到后续补充。因为事件对象的兼容性问题太多了,太繁琐了。
这篇我将引入一个私有的_fixEvent函数,add中将调用该函数。_fixEvent将修复(或称包装)原生事件对象,返回一个标准的统一接口的事件对象。如下
function _fixEvent( evt, el ) { var props = "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), len = props.length; function now() {return (new Date).getTime();} function returnFalse() {return false;} function returnTrue() {return true;} function Event( src ) { this.originalEvent = src; this.type = src.type; this.timeStamp = now(); } Event.prototype = { preventDefault: function() { this.isDefaultPrevented = returnTrue; var e = this.originalEvent; if( e.preventDefault ) { e.preventDefault(); } e.returnValue = false; }, stopPropagation: function() { this.isPropagationStopped = returnTrue; var e = this.originalEvent; if( e.stopPropagation ) { e.stopPropagation(); } e.cancelBubble = true; }, stopImmediatePropagation: function() { this.isImmediatePropagationStopped = returnTrue; this.stopPropagation(); }, isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse }; var originalEvent = evt; evt = new Event( originalEvent ); for(var i = len, prop; i;) { prop = props[ --i ]; evt[ prop ] = originalEvent[ prop ]; } if(!evt.target) { evt.target = evt.srcElement || document; } if( evt.target.nodeType === 3 ) { evt.target = evt.target.parentNode; } if( !evt.relatedTarget && evt.fromElement ) { evt.relatedTarget = evt.fromElement === evt.target ? evt.toElement : evt.fromElement; } if( evt.pageX == null && evt.clientX != null ) { var doc = document.documentElement, body = document.body; evt.pageX = evt.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); evt.pageY = evt.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); } if( !evt.which && ((evt.charCode || evt.charCode === 0) ? evt.charCode : evt.keyCode) ) { evt.which = evt.charCode || evt.keyCode; } if( !evt.metaKey && evt.ctrlKey ) { evt.metaKey = evt.ctrlKey; } if( !evt.which && evt.button !== undefined ) { evt.which = (evt.button & 1 ? 1 : ( evt.button & 2 ? 3 : ( evt.button & 4 ? 2 : 0 ) )); } if(!evt.currentTarget) evt.currentTarget = el; return evt; }
好了,现在你要
1,阻止事件默认行为,统一使用e.preventDefault()
2,停止冒泡,统一使用e. stopPropagation()
3,获取事件源,统一使用e.target
...
更多的差异性,不在这一一列举了。请参考我在google doc上整理的文档:各浏览器中事件对象差异性列表。呃,不能FQ的同学抱歉了。以下是我的事件模块最终版功能
1,最基本的添加、删除监听函数的事
function handle(){ //todo } E.add(document,'click',handle); E.remove(document,'click',handle);
2,为同一个元素添加多个handle时,执行有序。所有浏览器中依次弹出1,2,3,4,5
function handle1(){alert(1);} function handle2(){alert(2);} function handle3(){alert(3);} function handle4(){alert(4);} function handle5(){alert(5);} E.add(document,'click',handle1); E.add(document,'click',handle2); E.add(document,'click',handle3); E.add(document,'click',handle4); E.add(document,'click',handle5);
3,添加仅执行一次的handler
function handler(){ alert(1); } E.add(document,'click',handler,true);
4,删除元素指定类型的所有handler
function handle1(){ alert(1); } function handle2(){ alert(2); } E.add(document,'click',handle1); E.add(document,'click',handle2); E.remove(document,'click'); //删除了handle1和handle2
5,删除附加元素上的所有handler
function handle1(){ alert(1); } function handle2(){ alert(2); } E.add(document,'click',handle1); E.add(document,'mouseover',handle2); E.remove(document);
6,主动触发事件。如模拟用户点击
function handle(){ alert(1); } E.add(document,'click',handle); E.dispatch(document,'click');
7,解决事件对象的兼容性问题,如阻止默认行为,停止冒泡等(支持DOM2/3 events)