此模块用于提供自定义事件,并把实现此接口的对象变成一个事件发送器。
//================================================== // 事件发送器模块 //================================================== (function(global,DOC){ var dom = global[DOC.URL.replace(/(#.+|\W)/g,'')]; dom.define("emitter","data", function(){ var fireType = "", blank = "" var rhoverHack = /\bhover(\.\S+)?/, rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, reprop = /[a-z]/; var system = dom.event = { special:{},//用于处理个别的DOM事件 bind : function( types, handler, selector){ //它将在原生事件发送器或任何能成为事件发送器的普通JS对象添加一个名叫uniqueNumber的属性,用于关联一个缓存体, //把需要的数据储存到里面,而现在我们就把一个叫@events的对象储放都它里面, //而这个@event的表将用来放置各种事件类型与对应的回调函数 var target = this, events = dom._data( target) , nativeEmitter = dom["@emitter"] in target, all, tns ,type, namespace, special, handlerObj, handlers, fn; if(target.nodeType === 3 || target.nodeType === 8 || !types || typeof handler !=="function" || !events) return ; all = { handler:handler, uuid: dom.uuid++ } //确保UUID,bag与callback的UUID一致 all.handler.uuid = all.uuid; if(nativeEmitter ){ //处理DOM事件 fn = events.handle || (events.handle = function( e ) { return ((e || event).type !== fireType) ? system.handle.apply( fn.target, arguments ) :void 0; }); fn.target = target; types = types.replace( rhoverHack, "mouseover$1 mouseout$1" ) } events = events.events || (events.events = {}); //对多个事件进行绑定 types.replace(dom.rword,function(type){ tns = rtypenamespace.exec( type ) || []; type = tns[1];//取得事件类型 namespace = (tns[2] || "").split( "." ).sort();//取得命名空间 //事件冒充只用于原生事件发送器 special = nativeEmitter && system.special[ type ] || {}; type = (selector? special.delegateType : special.bindType ) || type; special = nativeEmitter && system.special[ type ] || {}; handlerObj = dom.mix({ type: type, origType: tns[1], selector: selector, namespace: namespace.join(".") }, all); //创建事件队列 handlers = events[ type ] = events[ type ] || []; //只有原生事件发送器才能进行DOM level2 多投事件绑定 if(nativeEmitter && !handlers.length ){ if (!special.setup || special.setup( target, selector, fn ) === false ) { // 为此元素这种事件类型绑定一个全局的回调,用户的回调则在此回调中执行 dom.bind(target,type,fn,!!selector) } } handlers.push( handlerObj ); }); }, unbind: function( types, handler, selector ) { var target = this, events = dom._data( target,"events") if(!events) return; var t, tns, type, namespace, origCount,nativeEmitter = dom["@emitter"] in target, j, special, handlers, handlerObj; types = nativeEmitter ? (types || "").replace( rhoverHack, "mouseover$1 mouseout$1" ) : types; types = (types || "").split(" "); for ( t = 0; t < types.length; t++ ) { tns = rtypenamespace.exec( types[t] ) || []; type = tns[1]; namespace = tns[2]; // 如果types只包含命名空间,则去掉所有拥有此命名空间的事件类型的回调 if ( !type ) { namespace = namespace? "." + namespace : ""; for ( j in events ) { system.unbind.call( target, j + namespace, handler, selector ); } return; } //如果使用事件冒充则找到其正确事件类型 special = system.special[ type ] || {}; type = (selector? special.delegateType : special.bindType ) || type; handlers = events[ type ] || []; origCount = handlers.length; namespace = namespace? namespace.split( "." ).sort().join(".") : null; //只有指定了命名空间,回调或选择器才能进入此分支 if ( handler || namespace || selector ) { for ( j = 0; j < handlers.length; j++ ) { handlerObj = handlers[ j ]; if ( !handler || handler.uuid === handlerObj.uuid ) { if ( !namespace || namespace === handlerObj.namespace ) { if ( !selector || selector === handlerObj.selector || selector === "**" && handlerObj.selector ) { handlers.splice( j--, 1 ); } } } } } else { //移除此类事件的所有回调 handlers.length = 0; } if (nativeEmitter && (handlers.length === 0 && origCount !== handlers.length) ) { if ( !special.teardown || special.teardown( target, selector, handler ) === false ) { dom.unbind( target, type, dom._data(target,"handle") ); } delete events[ type ]; } } if(dom.isEmptyObject(events)){ var handle = dom.removeData( target,"handle") ; handle.elem = null; dom.removeData( target,"events") ; } }, fire:function(event){ var target = this, namespace = [], type = event.type || event if ( type.indexOf( "." ) !== -1 ) { namespace = type.split("."); type = namespace.shift(); namespace.sort(); } var events = dom._data( target,"events") ,args = dom.slice(arguments,1) if(!events) return; event = (typeof event == "object" && "namespace" in event)? type : new jEvent(type); event.target = target; event.fireArgs = args; event.namespace = namespace.join( "." ); if( dom["@emitter"] in target){ var special = system.special[ type ] || {}; if ( special.fire && special.fire.call( target, event ) === false ) { return; } var cur = target, ontype = "on" + type; do{//模拟事件冒泡与执行内联事件 system.handle.call(cur, event); if (cur[ ontype ] && cur[ ontype ].call(cur) === false) { event.preventDefault(); } cur = cur.parentNode || cur.ownerDocument || cur === target.ownerDocument && global; } while (cur && !event.isPropagationStopped); if (!event.isDefaultPrevented) {//模拟默认行为 click() submit() reset() focus() blur() var old; if (ontype && target[ type ] && ((type !== "focus" && type !== "blur") || target.offsetWidth !== 0) && !target.document) { old = target[ ontype ]; if (old) { // 不用再触发内联事件 target[ ontype ] = null; } fireType = type; target[ type ](); } fireType = blank; if (old) { target[ ontype ] = old; } } }else{//普通对象的自定义事件 system.handle.call(target, event); } }, filter:function(cur, parent, expr){ for ( ; cur != parent; cur = cur.parentNode || parent ) { if(dom.matchesSelector(cur, expr)) return true } return false; }, handle: function( e ) { var event = system.fix( e || event ), handlers = dom._data(this,"events"); if ( handlers ) { handlers = handlers[event.type]||[] arguments[0] = event; event.currentTarget = this; var src = event.target, result, //取得参数(只有自定义才有多个参数) args = "fireArgs" in event ? [event].concat(event.fireArgs) : arguments; //复制数组以防影响下一次的操作 handlers = handlers.concat(); //开始进行拆包操作 for ( var i = 0, obj; obj = handlers[i++]; ) { //如果是事件代理,确保元素处于enabled状态,并且满足过滤条件 if ( !src.disabled && !(event.button && event.type === "click") && (!obj.selector || system.filter(src, this, obj.selector)) && (!event.namespace || event.namespace === obj.namespace ) ) { //取得回调函数 result = obj.handler.apply( src, args ); if ( result !== undefined ) { event.result = result; if ( result === false ) { event.preventDefault(); event.stopPropagation(); } } if ( event.isImmediatePropagationStopped ) { break; } } } } return event.result; }, fix :function(event){ if(!("namespace" in event)){ var originalEvent = event event = new jEvent(originalEvent); for(var prop in originalEvent){ //去掉所有方法与常量 if(typeof originalEvent[prop] !== "function" && reprop.test(prop)){ event[prop] = originalEvent[prop] } } event.wheelDelta = 0; //mousewheel if ("wheelDelta" in originalEvent){ var detail = originalEvent.wheelDelta; //opera 9x系列的滚动方向与IE保持一致,10后修正 if(global.opera && global.opera.version() < 10) detail = -detail; event.wheelDelta = Math.round(detail); //修正safari的浮点 bug }else { //DOMMouseScroll event.wheelDelta = -originalEvent.detail*40; } //如果不存在target属性,为它添加一个 if ( !event.target ) { // 判定鼠标事件按下的是哪个键,1 === left; 2 === middle; 3 === right event.which = event.button === 2 ? 3 : event.button === 4 ? 2 : 1; event.target = event.srcElement || DOC; } //如果事件源对象为文本节点,则置入其父元素 if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; } //如果不存在relatedTarget属性,为它添加一个 if ( !event.relatedTarget && event.fromElement ) { event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; } //如果不存在pageX/Y则结合clientX/Y做一双出来 if ( event.pageX == null && event.clientX != null ) { var doc = event.target.ownerDocument || DOC, html = doc.documentElement, body = doc.body; event.pageX = event.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html && html.clientLeft || body && body.clientLeft || 0); event.pageY = event.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html && html.clientTop || body && body.clientTop || 0); } // 为键盘事件添加which事件 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) { event.which = event.charCode || event.keyCode; } // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) if ( !event.metaKey && event.ctrlKey ) { event.metaKey = event.ctrlKey; } } return event; }, setup: dom.bind, teardown:dom.unbind } var jEvent = dom.Event = function ( event ) { this.originalEvent = event.substr ? {} : event; this.type = event.type || event; this.namespace = "";//用于判定是否为伪事件对象 }; // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jEvent.prototype = { constructor:jEvent, //http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/events.html#Conformance toString:function(){ return "[object Event]" }, preventDefault: function() { this.isDefaultPrevented = true; var e = this.originalEvent; // 如果存在preventDefault 那么就调用它 if ( e.preventDefault ) { e.preventDefault(); } // 如果存在returnValue 那么就将它设为false e.returnValue = false; return this; }, stopPropagation: function() { this.isPropagationStopped = true; var e = this.originalEvent; // 如果存在preventDefault 那么就调用它 if ( e.stopPropagation ) { e.stopPropagation(); } // 如果存在returnValue 那么就将它设为true e.cancelBubble = true; return this; }, stopImmediatePropagation: function() { this.isImmediatePropagationStopped = true; this.stopPropagation(); return this; } }; //事件发射体emitter的接口 //实现了这些接口的对象将具有注册事件和触发事件的功能 dom.emitter = {}; "bind,unbind,fire".replace(dom.rword,function(name){ dom.emitter[name] = function(){ system[name].apply(this, arguments); return this; } }); dom.emitter.uniqueNumber = ++dom.uuid; dom.emitter.defineEvents = function(names){ var events = []; if(typeof names == "string"){ events = names.match(dom.rword); }else if(dom.isArray(names)){ events = names; } events.forEach(function(name){ var method = 'on'+name.replace(/(^|_|:)([a-z])/g,function($, $1, $2) { return $2.toUpperCase(); }); if (!(method in this)) { this[method] = function() { return this.bind.apply(this, [].concat.apply([name],arguments)); }; } },this); } }); })(this,this.document); //2011.8.14 更改隐藏namespace,让自定义对象的回调函数也有事件对象 //2011.9.17 事件发送器增加一个uniqueID属性 //2011.9.21 重构bind与unbind方法 支持命名空间与多事件处理 //2011.9.27 uniqueID改为uniqueNumber 使用dom._data存取数据 //2011.9.29 简化bind与unbind