Event模块的设计思路
1、$.event.add(elem,types,handler,data,selector) 添加事件回调函数:
构造Data对象实现,其中,elemData.handle属性存放的回调函数通过elem.addEventListener方法绑定到元素上,页面事件触发时,执行该回调函数;elemData.events属性以对象形式存放事件类型、命名空间、捕获元素、回调的guid、回调函数的其他信息等。同时在$.event.dispatch()方法中,elemData.events对象用于生成elemData.handle回调函数的内部代码。
因为Data对象附着在elem元素上,可通过dataPriv.get(this,"events")方式获取,并进行后续使用、添加或移除操作。
// 添加绑定事件 add:function(elem,types,handler,data,selector){ var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData=dataPriv.get(elem);// 创建或获取Data对象,附着在jquery对象elem的属性中 if ( !elemData ){// 文本、注释节点不能设置Data对象 return; } if ( handler.handler ){// 回调函数设置为对象形式,handler属性为函数,selector为捕获节点 handleObjIn=handler; handler=handleObjIn.handler; selector=handleObjIn.selector; } // jQuery.find.matchesSelector(elem,expr) 判断元素elem是否满足表达式expr if ( selector ){ jQuery.find.matchesSelector(documentElement,selector); } // 绑定函数添加guid属性,以便解绑时确认删除绑定事件 if ( !handler.guid ){ handler.guid=jQuery.guid++; } // elemData.events未定义设置为空对象,通过events获取elemData.events;elemData.handle相同 // elemData.events[type]存储handleObj,包含事件类型、上下文、选择器、回调函数相关数据 // hanlers获取elemData.events[type]添加回调函数 // elemData.handle存储回调函数的触发方法dispatch,接着通过elemData.events获取回调 if ( !(events=elemData.events) ){ events=elemData.events={}; } if ( !(eventHandle=elemData.handle) ){ // jQuery.event.dispatch获取原生语句addEventListener绑定的回调函数,并且添加到Data对象中 // 传参为on语法回调函数的参数arguments,event对象内部构 eventHandle=elemData.handle=function(e){ // 页面尚未完成加载或者调用$ele.trigger()方法时,不用原生语句添加事件 return typeof jQuery!=="undefined" && jQuery.event.triggered!==e.type ? jQuery.event.dispatch.apply(elem,arguments) : undefined; }; } types=( types || "" ).match(rnotwhite) || [""];// 多事件以空格分割 t=types.length; while (t--){ // 事件类型配置项拆分所属事件类型名和命名空间 tmp=rtypenamespace.exec(types[t]) || []; type=origType=tmp[1];// 事件类型 namespaces=( tmp[2] || "" ).split(".").sort();// 命名空间 if ( !type ){ continue; } special=jQuery.event.special[type] || {};// 特殊事件focusin等经jquery转化成focus输出 // 获取浏览器原生的事件focusin、focusout等 type=(selector ? special.delegateType : special.bindType) || type; special=jQuery.event.special[type] || {}; // handleObj包含事件类型、回调函数等信息,存储在elemData.events[type]数组中 handleObj=jQuery.extend({ type:type,// 浏览器支持的事件类型 origType:origType,// jquery支持的事件类型 data:data,// 附带的数据 handler:handler,// 事件的回调函数 guid:handler.guid,// 回调函数的guid selector:selector,// 捕获的节点 needsContext:selector && jQuery.expr.match.needsContext.test(selector), namespace:namespaces.join(".")// 命名空间 },handleObjIn);// handleObjIn回调函数相关数据 // elemData.events[type]未定义设置为空数组,通过handlers获取elemData.events[type] // type为浏览器原生事件类型 if ( !(handlers=events[type]) ){ handlers=events[type]=[]; handlers.delegateCount=0; // special.setup方法以使focus、blur事件通过向文档节点挂载$ele.trigger实现获得、失去焦点 // addEventListener原生语句绑定事件,事件捕获功能由浏览器原生语句支持 if ( !special.setup || special.setup.call(elem,data,namespaces,eventHandle)===false ){ if ( elem.addEventListener ){// 浏览器原生语句绑定事件 elem.addEventListener(type,eventHandle); } } } // 默认special.add均为false,add方法对Data对象中回调队列进行改写 if ( special.add ){ special.add.call(elem,handleObj); if ( !handleObj.handler.guid ){ handleObj.handler.guid=handler.guid; } } // 有捕获元素,回调队列前插入,无则队列后添加;调用回调时dispatch、handlers方法获取handlers if ( selector ){ handlers.splice(handlers.delegateCount++,0,handleObj); } else { handlers.push(handleObj); } jQuery.event.global[type]=true; } },
2、$.event.dispatch(nativeEvent) 构造元素绑定的回调函数:
通过Dom接口addEventLIstener添加到elem元素上,当浏览器点击、悬停等事件触发时执行回调函数。
构造回调函数过程中,首先浏览器原生事件对象nativeEvent通过jQuery.event.fix方法封装为jQuery特有的event对象,且作为回调的首参;其余参数在add方法调用dispatch时带入。
回调函数执行前先执行sepcial.preDispatch函数,执行完成后执行sepcial.postDispatch函数。
通过$.event.handlers获取触发事件元素及其父元素的data对象上绑定的回调函数和执行上下文,通过event.isPropagationStoped方法判断是否阻止向上冒泡,以及event.isImmediatePropagationStoped方法旁观是否阻止同一元素的其他挂载函数,否则通过比较命名空间执行特殊的sepcial[origType].handle回调、或者$.event.handlers取出的回调,回调返回为false时阻止默认事件以及向上冒泡。
// 事件触发时获取回调函数队列并处理执行 // 首先执行有捕获元素selector设置的回调函数,再执行没有捕获元素的回调函数 // event.stopPropagation阻止向上冒泡触发事件,event.stopImmediatePropagation阻止元素后续的绑定事件 // dispatch方法内部this关键字为绑定事件元素 dispatch:function(nativeEvent){ // 获取jquery包装的event对象 var event=jQuery.event.fix(nativeEvent); var i, j, ret, matched, handleObj, handlerQueue, args=new Array(arguments.length), handlers=( dataPriv.get(this,"events") || {} )[event.type] || [],// 获取绑定函数 special=jQuery.event.special[event.type] || {}; args[0]=event; for ( i=1; i<arguments.length; i++ ) { args[i]=arguments[i];// 用户配置的回调函数参数 } event.delegateTarget=this;// 绑定事件的节点 // special.preDispatch、special.postDispatch钩子进行预处理、回调后处理 if ( special.preDispatch && special.preDispatch.call(this,event)===false ){ return; } // 获取Data对象中回调函数队列,及回调函数执行的上下文 handlerQueue=jQuery.event.handlers.call(this,event,handlers); i=0; // event.isPropagationStopped返回真值阻止事件冒泡,只执行捕获或绑定元素的事件 while ( ( matched=handlerQueue[i++] ) && !event.isPropagationStopped() ){ event.currentTarget=matched.elem; j=0; // event.isImmediatePropagationStopped返回真值阻止当前元素后续的绑定事件 while ( (handleObj=matched.handlers[j++]) && !event.isImmediatePropagationStopped() ){ if ( !event.rnamespace || event.rnamespace.test(handleObj.namespace) ){ event.handleObj=handleObj; event.data=handleObj.data; // 执行$ele.on方法添加的回调函数,this关键字为绑定事件元素,首参为event ret=( (jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler ) .apply(matched.elem,args); // $ele.on方法返回false阻止默认事件以及冒泡行为 if ( ret!==undefined ){ if ( ( event.result=ret )===false ){ event.preventDefault(); event.stopPropagation(); } } } } } if ( special.postDispatch ){ special.postDispatch.call(this,event); } return event.result; },
3、$.event.handlers(event,handlers)取出元素data对象中的回调函数:
获取当前元素及其父元素data中的回调函数及回调上下文。
// 获取绑定元素Data对象中存储的回调函数,以及该回调执行的上下文,即期望的或绑定事件的节点元素 // 返回handlerQueue数组个数由回调函数上下文个数决定,即捕获元素selector个数+1 // handlers方法内部this关键字为绑定事件元素 handlers:function(event,handlers){ var i, matches, sel, handleObj, handlerQueue=[], delegateCount=handlers.delegateCount,// $ele.on方法设置捕获元素的次数 cur=event.target; // Support: IE <=9 // Find delegate handlers // Black-hole SVG <use> instance trees (#13180) // // Support: Firefox <=42 // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343) if ( delegateCount && cur.nodeType && ( event.type!=="click" || isNaN(event.button) || event.button<1 ) ){ for ( ; cur!==this; cur=cur.parentNode || this ){ // Don't check non-elements (#13208) // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) if ( cur.nodeType===1 && ( cur.disabled!==true || event.type!=="click" ) ){ matches=[]; for ( i=0; i<delegateCount; i++ ){ handleObj=handlers[i]; sel=handleObj.selector+" "; if ( matches[sel]===undefined ){ matches[sel]=handleObj.needsContext ? jQuery(sel,this).index(cur)>-1 :// sel为数组形式,cur为其中一项 jQuery.find(sel,this,null,[cur]).length; } if ( matches[sel] ){ matches.push(handleObj); } } if ( matches.length ){ handlerQueue.push( {elem:cur,handlers:matches} ); } } } } // $ele.on、$ele.one方法未添加捕获元素,获取绑定的回调函数,以及该回调执行的上下文,即绑定元素 if ( delegateCount<handlers.length ){ handlerQueue.push( {elem:this, handlers:handlers.slice(delegateCount)} ); } return handlerQueue; },
4、$.event.remove(elem,types,handler,selector,mappedTypes)移除data对象的回调函数:
通过引用类型移除元素data对象中的回调函数,清空完毕后释放内存,并用浏览器原生语句removeEventListener方法解绑回调。
// 删除绑定事件,由回调guid、捕获元素、事件类型查询绑定事件并撤销 remove:function(elem,types,handler,selector,mappedTypes){ var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData=dataPriv.hasData(elem) && dataPriv.get(elem); if ( !elemData || !( events=elemData.events ) ){ return; } types=( types || "" ).match(rnotwhite) || [""]; t=types.length while ( t-- ){ tmp=rtypenamespace.exec(types[t]) || []; type=origType=tmp[1]; namespaces=( tmp[2] || "" ).split(".").sort(); // 当type赋值为空,解绑相同命名空间namespaces下所有事件 if ( !type ){ for ( type in events ){ jQuery.event.remove(elem,type+types[t],handler,selector,true ); } continue; } special=jQuery.event.special[type] || {}; type=( selector ? special.delegateType : special.bindType ) || type; handlers=events[type] || []; tmp=tmp[2] && new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)"); origCount=j=handlers.length; while ( j-- ){ handleObj=handlers[j];// 获取Data对象中的回调函数队列 if ( ( mappedTypes || origType===handleObj.origType ) &&// 相同事件类型或所有 ( !handler || handler.guid===handleObj.guid ) &&// 相同绑定函数或所有 ( !tmp || tmp.test(handleObj.namespace) ) &&// 相同命名空间或所有 ( !selector || selector===handleObj.selector ||// 相同捕获元素或所有 selector==="**" && handleObj.selector ) ){ handlers.splice(j,1); if ( handleObj.selector ){ handlers.delegateCount--; } if ( special.remove ){ special.remove.call(elem,handleObj); } } } // special.teardown方法更新文档节点绑定focus、blur事件的数量,没有事件时解绑函数 if ( origCount && !handlers.length ){ if ( !special.teardown || special.teardown.call(elem,namespaces,elemData.handle)===false ){ jQuery.removeEvent(elem,type,elemData.handle); } delete events[type]; } } if ( jQuery.isEmptyObject( events ) ){// 清空Data对象,节省内存 dataPriv.remove(elem,"handle events"); } },
5、$.event.trigger(event,data,elem,onlyHandlers) 以jQuery方式触发绑定函数:
先根据传参event是否jQuery封装的event对象,若不是,通过传参event获取jQuery封装的event对象。该event对象添加isTrigger属性,为真值时使用$.event.trigger触发事件,为否值时由浏览器点击等事件触发。
若当前事件类型的sepcial.trigger函数存在,执行该函数并返回,已知focus/blur事件为此种情况,仅得到或失去焦点。其他情况,先以eventPath数组形式获取存储向上冒泡的元素路径,接着获取data对象中各元素的回调函数并执行,以及元素路径上eventPath[i][ontype]用浏览器原生语句绑定在元素上的回调函数(该回调返回否值,阻止浏览器默认事件触发),接着根据sepcial._default函数返回值为否的情况,触发调用trigger方法元素elem[ontype]函数。
// onlyHandlers阻止向上冒泡 trigger:function(event,data,elem,onlyHandlers){ var i, cur, tmp, bubbleType, ontype, handle, special, eventPath=[elem || document], type=hasOwn.call(event,"type") ? event.type : event, namespaces=hasOwn.call(event,"namespace") ? event.namespace.split(".") : []; cur=tmp=elem=elem || document; if ( elem.nodeType===3 || elem.nodeType===8 ){// 文本节点和注释节点不能触发事件 return; } // focus/blur事件触发过程中,focusin/focusout事件不触发 if ( rfocusMorph.test(type+jQuery.event.triggered) ){ return; } if ( type.indexOf(".")>-1 ){ namespaces=type.split("."); type=namespaces.shift(); namespaces.sort(); } ontype=type.indexOf(":")<0 && "on"+type; // 获取或创建jquery封装的event对象 event=event[jQuery.expando] ? event : new jQuery.Event(type,typeof event==="object" && event); // event.isTrigger为真值时,由用户调用$ele.trigger方法触发,否则为浏览器事件自动出阿发 event.isTrigger=onlyHandlers ? 2 : 3; event.namespace=namespaces.join("."); // 以"."号分割的字符串中包含namespaces以"."号分割后的各子字符串 event.rnamespace=event.namespace ? new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)") : null; event.result=undefined; if ( !event.target ) { event.target=elem; } data=data==null ? [event] : jQuery.makeArray(data,[event]);// 作为参数传入回调函数 // special.trigger输入框获得focus或失去blur焦点,复选框选中click事件触发,阻止向上冒泡 special=jQuery.event.special[type] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply(elem,data)===false ){ return; } // eventPath添加向上冒泡元素路径 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow(elem) ){ bubbleType=special.delegateType || type; if ( !rfocusMorph.test(bubbleType+type) ){// rfocusMorph函数校验blur、focus返回true cur=cur.parentNode; } for ( ; cur; cur=cur.parentNode ){ eventPath.push(cur); tmp=cur; } if ( tmp===( elem.ownerDocument || document ) ){ eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // 执行回调函数,按条件执行浏览器原生的事件处理函数 i=0; while ( ( cur=eventPath[i++] ) && !event.isPropagationStopped() ){ event.type=i>1 ? bubbleType : special.bindType || type; // 获取绑定回调函数并执行,以cur作为上下文 handle=( dataPriv.get(cur,"events") || {} )[event.type] && dataPriv.get(cur,"handle"); if ( handle ){ handle.apply(cur,data); } // 获取并执行浏览器原生事件cur[ontype],该事件函数返回否值时,阻止浏览器默认事件执行 handle=ontype && cur[ontype]; if ( handle && handle.apply && acceptData(cur) ){ event.result=handle.apply(cur,data); if ( event.result===false ){ event.preventDefault(); } } } event.type=type; if ( !onlyHandlers && !event.isDefaultPrevented() ){ // special._default返回为真值时,不执行elem[type] // special._default为否则或其返回为否值时,执行elem[type] if ( ( !special._default || special._default.apply(eventPath.pop(),data)===false ) && acceptData(elem) ){ // 获取并执行浏览器原生事件elem[type],执行过程中缓存elem[ontype] if ( ontype && jQuery.isFunction(elem[type]) && !jQuery.isWindow(elem) ){ tmp=elem[ontype]; if ( tmp ){ elem[ontype]=null; } jQuery.event.triggered=type; elem[type](); jQuery.event.triggered=undefined; if ( tmp ){ elem[ontype]=tmp; } } } } return event.result; },
5、jQuery封装的event对象,构造函数为$.Event:
继承原生event对象的属性如offsetX等,按键which属性通过改写后获得。
添加preventDefault方法用于阻止默认事件触发,isDefaultPrevented用于检测默认事件是否不予触发。
stopPropagation方法用于阻止事件冒泡,isPropagationStopped用于检测向上冒泡是否已阻止。
stopImmediatePropagation方法用于阻止同一元素的后续事件触发,isImmediatePropagationStopped检测是否阻止。
// 调用e.preventDefault方法时,改变状态返回函数e.isDefaultPrevented的输出值 // 从而对回调函数产生影响,stopPropagation、stopImmediatePropagation方法相同 jQuery.Event.prototype={ constructor:jQuery.Event, isDefaultPrevented:returnFalse, isPropagationStopped:returnFalse, isImmediatePropagationStopped:returnFalse, isSimulated:false preventDefault:function(){// 阻止默认事件 var e=this.originalEvent; this.isDefaultPrevented=returnTrue; if ( e && !this.isSimulated ){ e.preventDefault();// 调用浏览器原生语句阻止默认事件 } }, stopPropagation:function(){// 阻止向上冒泡 var e=this.originalEvent; this.isPropagationStopped=returnTrue; if ( e && !this.isSimulated ){ e.stopPropagation(); } }, stopImmediatePropagation:function(){// 阻止后续同类事件执行 var e=this.originalEvent; this.isImmediatePropagationStopped=returnTrue; if ( e && !this.isSimulated ){ e.stopImmediatePropagation(); } this.stopPropagation(); } }; // 回调函数参数event属性设置取值、赋值,模块内立即执行 jQuery.each({ altKey: true, bubbles: true, cancelable: true, changedTouches: true, ctrlKey: true, detail: true, eventPhase: true, metaKey: true, pageX: true, pageY: true, shiftKey: true, view: true, "char": true, charCode: true, key: true, keyCode: true, button: true, buttons: true, clientX: true, clientY: true, offsetX: true, offsetY: true, pointerId: true, pointerType: true, screenX: true, screenY: true, targetTouches: true, toElement: true, touches: true, which: function( event ) { var button = event.button; // Add which for key events if ( event.which==null && rkeyEvent.test(event.type) ){ return event.charCode!=null ? event.charCode : event.keyCode; } // Add which for click: 1 === left; 2 === middle; 3 === right if ( !event.which && button!==undefined && rmouseEvent.test(event.type) { return ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); } return event.which; } },jQuery.event.addProp);
6、$.event.sepcial特殊事件处理:
special:{ /** * sepcial[type].trigger: $ele.trigger触发事件时使用,避过注册事件的执行和冒泡 * 只执行sepcial[type].trigger方法,包含得到失去焦点、复选框点选,focus、blur、click * sepcial[type].setup: 执行$ele.on方法时调用,支持事件类型为focus、blur * 将jQuery.event.simulate方法作为回调绑定在文档节点上, * 通过simulate触发jQuery.event.trigger方法,进而触发special.trigger执行 * event.target单纯获得、失去焦点,情形类同没有冒泡效果的事件 * sepcial[type].teardown: 执行$ele.remove方法时调用,支持事件类型为focus、blur * 文档节点缓存focus、blur事件数减1,文档节点解绑jQuery.event.trigger方法 * sepcial[type]._default: $ele.trigger触发事件时调用,返回为真,阻止ele[event.type]执行 * 当ele为a标签时,不触发ele["click"]函数跳链接 * special[type].preDispatch:绑定函数执行完成前调用 * special[type].postDispatch:绑定函数执行完成后调用 * special[type].remove: 移除回调函数时执行 * special[type]._default:返回真值阻止默认事件,已知a标签点击时不跳连接 */ load:{ // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus:{ // $.trigger("focus")阻止事件向上冒泡,只实现元素获得焦点 trigger:function(){ if ( this!==safeActiveElement() && this.focus ){ this.focus(); return false; } }, // focus类型注册的事件添加到elemData.events.focusin对象中,触发focus事件时也不取出 delegateType:"focusin" }, blur: { // $.trigger("focus")阻止事件向上冒泡,只实现元素失去焦点 trigger:function(){ if ( this===safeActiveElement() && this.blur ){ this.blur(); return false; } }, delegateType:"focusout" }, click:{ // $.trigger("click")阻止事件向上冒泡,只实现复选框元素点中 trigger:function(){ if ( this.type==="checkbox" && this.click && jQuery.nodeName(this,"input") ){ this.click(); return false; } }, // $ele.trigger方法触发时间,ele为a标签时返回为真,阻止ele["click"]执行,不触发跳链接动作 _default:function(event){ return jQuery.nodeName(event.target,"a"); } }, beforeunload:{// 离开页面事件,绑定函数返回值作为页面alert弹窗提示,原理不明? postDispatch:function(event){ if ( event.result!==undefined && event.originalEvent ){ event.originalEvent.returnValue=event.result; } } } }
Event模块的对外接口
1、$ele.on(types,selector,data,fn) 绑定事件:
types可设置命名空间,selector捕获节点,data携带数据,fn回调函数,通过点击等事件触发时执行。
2、$ele.one(types,selector,data,fn) 绑定事件:
types可设置命名空间,selector捕获节点,data携带数据,fn回调函数,通过点击等事件触发时执行,只执行一次。
3、$ele.off(types,selector,fn) 绑定事件:
通过事件类型、命名空间、捕获节点、回调的guid解绑回调函数。
jQuery.fn.extend({ on:function(types,selector,data,fn){ return on(this,types,selector,data,fn); }, one:function(types,selector,data,fn){ return on(this,types,selector,data,fn,1); }, // off方法除了可以设置参数为待解绑的事件类型以外,还可以针对事件解绑回调函数 off:function(types,selector,fn){ var handleObj,type; if ( types && types.preventDefault && types.handleObj ){ handleObj=types.handleObj jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types==="object" ){ for ( type in types ){ this.off(type,selector,types[type]); } return this; } if ( selector===false || typeof selector==="function" ){ fn=selector; selector=undefined; } if ( fn===false ){ fn=returnFalse; } return this.each( function(){ jQuery.event.remove(this,types,fn,selector); }); } });
4、$ele.trigger(type,data) 触发事件,以jquery方式,可以是多个元素的事件同时触发;
5、$ele.triggerHandler(type,data) 以首个触发事件,以jquery方式,阻止向上冒泡:
jQuery.fn.extend({ // $ele.trigger方法冒泡执行每个元素的事件,冒泡通过构建eventPath父级元素集合实现 trigger:function(type,data){ return this.each(function(){ jQuery.event.trigger(type,data,this); }); }, // $ele.triggerHandler方法执行首个元素的事件,并且阻止向上冒泡事件触发 triggerHandler: function(type,data){ var elem=this[0]; if ( elem ){ return jQuery.event.trigger(type,data,elem,true); } } });
6、$ele.blur(data,fn) 调用$ele.on或$ele.trigger快速绑定或触发事件:
// $ele.change等快速构建浏览器事件方法的实现,有参数时绑定事件,无参数时触发事件 // 调用$ele.on、$ele.trigger方法实现 // contextmenu鼠标右键事件,返回false禁用浏览器原生鼠标右键事件 jQuery.each( ("blur focus focusin focusout resize scroll click dblclick "+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave "+ "change select submit keydown keypress keyup contextmenu").split(" "), function(i,name){ jQuery.fn[name]=function(data,fn){ return arguments.length>0 ? this.on(name,null,data,fn) : this.trigger(name); }; }); // $ele.hover方法只是绑定鼠标移入移出事件,不触发 // 单参数时移出绑定函数和移入绑定函数相同 jQuery.fn.extend({ hover:function(fnOver,fnOut){ return this.mouseenter(fnOver).mouseleave(fnOut || fnOver); } });
Event模块源码
1、$ele.on|one|trigger、$ele.event.add|remove|dispatch|handlers|remove、$.EVENT:
define([ "./core", "./var/document", "./var/documentElement", "./var/rnotwhite",// 匹配非空字符串 "./var/slice", "./data/var/dataPriv",// 创建新的Data对象 "./core/init", "./selector" ],function(jQuery,document,documentElement,rnotwhite,slice,dataPriv){ "use strict"; var rkeyEvent=/^key/,// 键盘事件 rmouseEvent=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,// 鼠标事件 rtypenamespace=/^([^.]*)(?:\.(.+)|)/;// .号之前事件类型,.之后命名空间 function returnTrue(){ return true; } function returnFalse(){ return false; } // Support: IE <=9 only 返回当前获得焦点的元素 function safeActiveElement(){ try{ return document.activeElement; }catch(err){} } // $ele.on、$ele.one方法处理参数中间件,完成参数处理后,移交给$.event.add方法 function on(elem,types,selector,data,fn,one){ var origFn,type; // 参数types为对象形式,事件类型为键,绑定函数为值 if ( typeof types==="object" ){ if ( typeof selector!=="string" ){// 事件捕获元素selector落空 data=data || selector; selector=undefined; } for ( type in types ){ on(elem,type,selector,data,types[type],one); } return elem; } if ( data==null && fn==null ){// 可选项落空 fn=selector; data=selector=undefined; }else if( fn==null ){ if ( typeof selector==="string" ){ fn=data; data=undefined; }else{ fn=data; data=selector; selector=undefined; } } if ( fn===false ) fn=returnFalse; }else if( !fn ){ return elem; } if ( one===1 ){ origFn=fn; fn=function(event){ // Can use an empty set, since event contains the info jQuery().off(event); return origFn.apply(this,arguments); }; // Use same guid so caller can remove using origFn fn.guid=origFn.guid || ( origFn.guid=jQuery.guid++ ); } return elem.each( function(){ // 通过原生语句绑定事件,回调函数构建Data对象附着在jquery元素对象中 jQuery.event.add(this,types,fn,data,selector); }); } jQuery.event={ global:{},// 记录已绑定的事件类型,用于事件优化??? // 添加绑定事件 add:function(elem,types,handler,data,selector){ var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData=dataPriv.get(elem);// 创建或获取Data对象,附着在jquery对象elem的属性中 if ( !elemData ){// 文本、注释节点不能设置Data对象 return; } if ( handler.handler ){// 回调函数设置为对象形式,handler属性为函数,selector为捕获节点 handleObjIn=handler; handler=handleObjIn.handler; selector=handleObjIn.selector; } // jQuery.find.matchesSelector(elem,expr) 判断元素elem是否满足表达式expr if ( selector ){ jQuery.find.matchesSelector(documentElement,selector); } // 绑定函数添加guid属性,以便解绑时确认删除绑定事件 if ( !handler.guid ){ handler.guid=jQuery.guid++; } // elemData.events未定义设置为空对象,通过events获取elemData.events;elemData.handle相同 // elemData.events[type]存储handleObj,包含事件类型、上下文、选择器、回调函数相关数据 // hanlers获取elemData.events[type]添加回调函数 // elemData.handle存储回调函数的触发方法dispatch,接着通过elemData.events获取回调 if ( !(events=elemData.events) ){ events=elemData.events={}; } if ( !(eventHandle=elemData.handle) ){ // jQuery.event.dispatch获取原生语句addEventListener绑定的回调函数,并且添加到Data对象中 // 传参为on语法回调函数的参数arguments,event对象内部构 eventHandle=elemData.handle=function(e){ // 页面尚未完成加载或者调用$ele.trigger()方法时,不用原生语句添加事件 return typeof jQuery!=="undefined" && jQuery.event.triggered!==e.type ? jQuery.event.dispatch.apply(elem,arguments) : undefined; }; } types=( types || "" ).match(rnotwhite) || [""];// 多事件以空格分割 t=types.length; while (t--){ // 事件类型配置项拆分所属事件类型名和命名空间 tmp=rtypenamespace.exec(types[t]) || []; type=origType=tmp[1];// 事件类型 namespaces=( tmp[2] || "" ).split(".").sort();// 命名空间 if ( !type ){ continue; } special=jQuery.event.special[type] || {};// 特殊事件focusin等经jquery转化成focus输出 // 获取浏览器原生的事件focusin、focusout等 type=(selector ? special.delegateType : special.bindType) || type; special=jQuery.event.special[type] || {}; // handleObj包含事件类型、回调函数等信息,存储在elemData.events[type]数组中 handleObj=jQuery.extend({ type:type,// 浏览器支持的事件类型 origType:origType,// jquery支持的事件类型 data:data,// 附带的数据 handler:handler,// 事件的回调函数 guid:handler.guid,// 回调函数的guid selector:selector,// 捕获的节点 needsContext:selector && jQuery.expr.match.needsContext.test(selector), namespace:namespaces.join(".")// 命名空间 },handleObjIn);// handleObjIn回调函数相关数据 // elemData.events[type]未定义设置为空数组,通过handlers获取elemData.events[type] // type为浏览器原生事件类型 if ( !(handlers=events[type]) ){ handlers=events[type]=[]; handlers.delegateCount=0; // special.setup方法以使focus、blur事件通过向文档节点挂载$ele.trigger实现获得、失去焦点 // addEventListener原生语句绑定事件,事件捕获功能由浏览器原生语句支持 if ( !special.setup || special.setup.call(elem,data,namespaces,eventHandle)===false ){ if ( elem.addEventListener ){// 浏览器原生语句绑定事件 elem.addEventListener(type,eventHandle); } } } // 默认special.add均为false,add方法对Data对象中回调队列进行改写 if ( special.add ){ special.add.call(elem,handleObj); if ( !handleObj.handler.guid ){ handleObj.handler.guid=handler.guid; } } // 有捕获元素,回调队列前插入,无则队列后添加;调用回调时dispatch、handlers方法获取handlers if ( selector ){ handlers.splice(handlers.delegateCount++,0,handleObj); } else { handlers.push(handleObj); } jQuery.event.global[type]=true; } }, // 删除绑定事件,由回调guid、捕获元素、事件类型查询绑定事件并撤销 remove:function(elem,types,handler,selector,mappedTypes){ var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData=dataPriv.hasData(elem) && dataPriv.get(elem); if ( !elemData || !( events=elemData.events ) ){ return; } types=( types || "" ).match(rnotwhite) || [""]; t=types.length while ( t-- ){ tmp=rtypenamespace.exec(types[t]) || []; type=origType=tmp[1]; namespaces=( tmp[2] || "" ).split(".").sort(); // 当type赋值为空,解绑相同命名空间namespaces下所有事件 if ( !type ){ for ( type in events ){ jQuery.event.remove(elem,type+types[t],handler,selector,true ); } continue; } special=jQuery.event.special[type] || {}; type=( selector ? special.delegateType : special.bindType ) || type; handlers=events[type] || []; tmp=tmp[2] && new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)"); origCount=j=handlers.length; while ( j-- ){ handleObj=handlers[j];// 获取Data对象中的回调函数队列 if ( ( mappedTypes || origType===handleObj.origType ) &&// 相同事件类型或所有 ( !handler || handler.guid===handleObj.guid ) &&// 相同绑定函数或所有 ( !tmp || tmp.test(handleObj.namespace) ) &&// 相同命名空间或所有 ( !selector || selector===handleObj.selector ||// 相同捕获元素或所有 selector==="**" && handleObj.selector ) ){ handlers.splice(j,1); if ( handleObj.selector ){ handlers.delegateCount--; } if ( special.remove ){ special.remove.call(elem,handleObj); } } } // special.teardown方法更新文档节点绑定focus、blur事件的数量,没有事件时解绑函数 if ( origCount && !handlers.length ){ if ( !special.teardown || special.teardown.call(elem,namespaces,elemData.handle)===false ){ jQuery.removeEvent(elem,type,elemData.handle); } delete events[type]; } } if ( jQuery.isEmptyObject( events ) ){// 清空Data对象,节省内存 dataPriv.remove(elem,"handle events"); } }, // 事件触发时获取回调函数队列并处理执行 // 首先执行有捕获元素selector设置的回调函数,再执行没有捕获元素的回调函数 // event.stopPropagation阻止向上冒泡触发事件,event.stopImmediatePropagation阻止元素后续的绑定事件 // dispatch方法内部this关键字为绑定事件元素 dispatch:function(nativeEvent){ // 获取jquery包装的event对象 var event=jQuery.event.fix(nativeEvent); var i, j, ret, matched, handleObj, handlerQueue, args=new Array(arguments.length), handlers=( dataPriv.get(this,"events") || {} )[event.type] || [],// 获取绑定函数 special=jQuery.event.special[event.type] || {}; args[0]=event; for ( i=1; i<arguments.length; i++ ) { args[i]=arguments[i];// 用户配置的回调函数参数 } event.delegateTarget=this;// 绑定事件的节点 // special.preDispatch、special.postDispatch钩子进行预处理、回调后处理 if ( special.preDispatch && special.preDispatch.call(this,event)===false ){ return; } // 获取Data对象中回调函数队列,及回调函数执行的上下文 handlerQueue=jQuery.event.handlers.call(this,event,handlers); i=0; // event.isPropagationStopped返回真值阻止事件冒泡,只执行捕获或绑定元素的事件 while ( ( matched=handlerQueue[i++] ) && !event.isPropagationStopped() ){ event.currentTarget=matched.elem; j=0; // event.isImmediatePropagationStopped返回真值阻止当前元素后续的绑定事件 while ( (handleObj=matched.handlers[j++]) && !event.isImmediatePropagationStopped() ){ if ( !event.rnamespace || event.rnamespace.test(handleObj.namespace) ){ event.handleObj=handleObj; event.data=handleObj.data; // 执行$ele.on方法添加的回调函数,this关键字为绑定事件元素,首参为event ret=( (jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler ) .apply(matched.elem,args); // $ele.on方法返回false阻止默认事件以及冒泡行为 if ( ret!==undefined ){ if ( ( event.result=ret )===false ){ event.preventDefault(); event.stopPropagation(); } } } } } if ( special.postDispatch ){ special.postDispatch.call(this,event); } return event.result; }, // 获取绑定元素Data对象中存储的回调函数,以及该回调执行的上下文,即期望的或绑定事件的节点元素 // 返回handlerQueue数组个数由回调函数上下文个数决定,即捕获元素selector个数+1 // handlers方法内部this关键字为绑定事件元素 handlers:function(event,handlers){ var i, matches, sel, handleObj, handlerQueue=[], delegateCount=handlers.delegateCount,// $ele.on方法设置捕获元素的次数 cur=event.target; // Support: IE <=9 // Find delegate handlers // Black-hole SVG <use> instance trees (#13180) // // Support: Firefox <=42 // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343) if ( delegateCount && cur.nodeType && ( event.type!=="click" || isNaN(event.button) || event.button<1 ) ){ for ( ; cur!==this; cur=cur.parentNode || this ){ // Don't check non-elements (#13208) // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) if ( cur.nodeType===1 && ( cur.disabled!==true || event.type!=="click" ) ){ matches=[]; for ( i=0; i<delegateCount; i++ ){ handleObj=handlers[i]; sel=handleObj.selector+" "; if ( matches[sel]===undefined ){ matches[sel]=handleObj.needsContext ? jQuery(sel,this).index(cur)>-1 :// sel为数组形式,cur为其中一项 jQuery.find(sel,this,null,[cur]).length; } if ( matches[sel] ){ matches.push(handleObj); } } if ( matches.length ){ handlerQueue.push( {elem:cur,handlers:matches} ); } } } } // $ele.on、$ele.one方法未添加捕获元素,获取绑定的回调函数,以及该回调执行的上下文,即绑定元素 if ( delegateCount<handlers.length ){ handlerQueue.push( {elem:this, handlers:handlers.slice(delegateCount)} ); } return handlerQueue; }, // 封装的jquery对象(回调函数参数event),配置赋值set、取值get方法 addProp:function(name,hook){ Object.defineProperty(jQuery.Event.prototype,name,{ enumerable:true,// 可枚举 configurable:true,// 可配置 // hook为函数时通过hook改写原生事件对象后输出,否则输出原生事件对象 get:jQuery.isFunction(hook) ? function(){ if ( this.originalEvent ){// this.originalEvent原生事件对象 return hook(this.originalEvent); } } : function(){ if ( this.originalEvent ){ return this.originalEvent[name]; } }, set:function(value){ Object.defineProperty(this,name,{ enumerable:true, configurable:true, writable:true, value:value }); } } ); }, // 获取jquery包装的event对象,作为绑定函数的首参 fix:function(originalEvent){ return originalEvent[jQuery.expando] ? originalEvent : new jQuery.Event(originalEvent); }, special:{ /** * sepcial[type].trigger: $ele.trigger触发事件时使用,避过注册事件的执行和冒泡 * 只执行sepcial[type].trigger方法,包含得到失去焦点、复选框点选,focus、blur、click * sepcial[type].setup: 执行$ele.on方法时调用,支持事件类型为focus、blur * 将jQuery.event.simulate方法作为回调绑定在文档节点上, * 通过simulate触发jQuery.event.trigger方法,进而触发special.trigger执行 * event.target单纯获得、失去焦点,情形类同没有冒泡效果的事件 * sepcial[type].teardown: 执行$ele.remove方法时调用,支持事件类型为focus、blur * 文档节点缓存focus、blur事件数减1,文档节点解绑jQuery.event.trigger方法 * sepcial[type]._default: $ele.trigger触发事件时调用,返回为真,阻止ele[event.type]执行 * 当ele为a标签时,不触发ele["click"]函数跳链接 * special[type].preDispatch:绑定函数执行完成前调用 * special[type].postDispatch:绑定函数执行完成后调用 * special[type].remove: 移除回调函数时执行 * special[type]._default:返回真值阻止默认事件,已知a标签点击时不跳连接 */ load:{ // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus:{ // $.trigger("focus")阻止事件向上冒泡,只实现元素获得焦点 trigger:function(){ if ( this!==safeActiveElement() && this.focus ){ this.focus(); return false; } }, // focus类型注册的事件添加到elemData.events.focusin对象中,触发focus事件时也不取出 delegateType:"focusin" }, blur: { // $.trigger("focus")阻止事件向上冒泡,只实现元素失去焦点 trigger:function(){ if ( this===safeActiveElement() && this.blur ){ this.blur(); return false; } }, delegateType:"focusout" }, click:{ // $.trigger("click")阻止事件向上冒泡,只实现复选框元素点中 trigger:function(){ if ( this.type==="checkbox" && this.click && jQuery.nodeName(this,"input") ){ this.click(); return false; } }, // $ele.trigger方法触发时间,ele为a标签时返回为真,阻止ele["click"]执行,不触发跳链接动作 _default:function(event){ return jQuery.nodeName(event.target,"a"); } }, beforeunload:{// 离开页面事件,绑定函数返回值作为页面alert弹窗提示,原理不明? postDispatch:function(event){ if ( event.result!==undefined && event.originalEvent ){ event.originalEvent.returnValue=event.result; } } } } }; // 解除元素的绑定事件 jQuery.removeEvent=function(elem,type,handle){ // This "if" is needed for plain objects if ( elem.removeEventListener ){ elem.removeEventListener(type,handle); } }; // 创建jquery封装的event对象,添加preventDefault等方法以及which等属性,作为回调函数的首参 // src为浏览器原生事件对象this.originalEvent,或单纯的事件类型 jQuery.Event=function(src,props){ if ( !(this instanceof jQuery.Event) ){ return new jQuery.Event(src,props); } if ( src && src.type ){ this.originalEvent=src; this.type=src.type; // 浏览器默认阻止 // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented=src.defaultPrevented || src.defaultPrevented===undefined && // Support: Android <=2.3 only src.returnValue===false ? returnTrue : returnFalse; // 实际触发事件的元素,文本元素改为其父节点 this.target=( src.target && src.target.nodeType===3 ) ? src.target.parentNode : src.target; this.currentTarget=src.currentTarget; this.relatedTarget=src.relatedTarget; }else{ this.type=src; } if ( props ){ jQuery.extend(this,props); } this.timeStamp=src && src.timeStamp || jQuery.now(); this[jQuery.expando]=true; }; // 调用e.preventDefault方法时,改变状态返回函数e.isDefaultPrevented的输出值 // 从而对回调函数产生影响,stopPropagation、stopImmediatePropagation方法相同 jQuery.Event.prototype={ constructor:jQuery.Event, isDefaultPrevented:returnFalse, isPropagationStopped:returnFalse, isImmediatePropagationStopped:returnFalse, isSimulated:false preventDefault:function(){// 阻止默认事件 var e=this.originalEvent; this.isDefaultPrevented=returnTrue; if ( e && !this.isSimulated ){ e.preventDefault();// 调用浏览器原生语句阻止默认事件 } }, stopPropagation:function(){// 阻止向上冒泡 var e=this.originalEvent; this.isPropagationStopped=returnTrue; if ( e && !this.isSimulated ){ e.stopPropagation(); } }, stopImmediatePropagation:function(){// 阻止后续同类事件执行 var e=this.originalEvent; this.isImmediatePropagationStopped=returnTrue; if ( e && !this.isSimulated ){ e.stopImmediatePropagation(); } this.stopPropagation(); } }; // 回调函数参数event属性设置取值、赋值,模块内立即执行 jQuery.each({ altKey: true, bubbles: true, cancelable: true, changedTouches: true, ctrlKey: true, detail: true, eventPhase: true, metaKey: true, pageX: true, pageY: true, shiftKey: true, view: true, "char": true, charCode: true, key: true, keyCode: true, button: true, buttons: true, clientX: true, clientY: true, offsetX: true, offsetY: true, pointerId: true, pointerType: true, screenX: true, screenY: true, targetTouches: true, toElement: true, touches: true, which: function( event ) { var button = event.button; // Add which for key events if ( event.which==null && rkeyEvent.test(event.type) ){ return event.charCode!=null ? event.charCode : event.keyCode; } // Add which for click: 1 === left; 2 === middle; 3 === right if ( !event.which && button!==undefined && rmouseEvent.test(event.type) { return ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); } return event.which; } },jQuery.event.addProp); // 模块中执行,jQuery.event.special添加属性 jQuery.each({ mouseenter:"mouseover", mouseleave:"mouseout", pointerenter:"pointerover", pointerleave:"pointerout" },function(orig,fix){ jQuery.event.special[orig]={ delegateType:fix, bindType:fix, handle: function( event ) { var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj; // For mouseenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; }); jQuery.fn.extend({ on:function(types,selector,data,fn){ return on(this,types,selector,data,fn); }, one:function(types,selector,data,fn){ return on(this,types,selector,data,fn,1); }, // off方法除了可以设置参数为待解绑的事件类型以外,还可以针对事件解绑回调函数 off:function(types,selector,fn){ var handleObj,type; if ( types && types.preventDefault && types.handleObj ){ handleObj=types.handleObj jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types==="object" ){ for ( type in types ){ this.off(type,selector,types[type]); } return this; } if ( selector===false || typeof selector==="function" ){ fn=selector; selector=undefined; } if ( fn===false ){ fn=returnFalse; } return this.each( function(){ jQuery.event.remove(this,types,fn,selector); }); } }); return jQuery; });
2、$ele.trigger、$.event.trigger:
define([ "../core", "../var/document", "../data/var/dataPriv", "../data/var/acceptData", "../var/hasOwn",// 获取对象属性 "../event" ],function(jQuery,document,dataPriv,acceptData,hasOwn){ "use strict"; var rfocusMorph=/^(?:focusinfocus|focusoutblur)$/; jQuery.extend(jQuery.event,{ // onlyHandlers阻止向上冒泡 trigger:function(event,data,elem,onlyHandlers){ var i, cur, tmp, bubbleType, ontype, handle, special, eventPath=[elem || document], type=hasOwn.call(event,"type") ? event.type : event, namespaces=hasOwn.call(event,"namespace") ? event.namespace.split(".") : []; cur=tmp=elem=elem || document; if ( elem.nodeType===3 || elem.nodeType===8 ){// 文本节点和注释节点不能触发事件 return; } // focus/blur事件触发过程中,focusin/focusout事件不触发 if ( rfocusMorph.test(type+jQuery.event.triggered) ){ return; } if ( type.indexOf(".")>-1 ){ namespaces=type.split("."); type=namespaces.shift(); namespaces.sort(); } ontype=type.indexOf(":")<0 && "on"+type; // 获取或创建jquery封装的event对象 event=event[jQuery.expando] ? event : new jQuery.Event(type,typeof event==="object" && event); // event.isTrigger为真值时,由用户调用$ele.trigger方法触发,否则为浏览器事件自动出阿发 event.isTrigger=onlyHandlers ? 2 : 3; event.namespace=namespaces.join("."); // 以"."号分割的字符串中包含namespaces以"."号分割后的各子字符串 event.rnamespace=event.namespace ? new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)") : null; event.result=undefined; if ( !event.target ) { event.target=elem; } data=data==null ? [event] : jQuery.makeArray(data,[event]);// 作为参数传入回调函数 // special.trigger输入框获得focus或失去blur焦点,复选框选中click事件触发,阻止向上冒泡 special=jQuery.event.special[type] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply(elem,data)===false ){ return; } // eventPath添加向上冒泡元素路径 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow(elem) ){ bubbleType=special.delegateType || type; if ( !rfocusMorph.test(bubbleType+type) ){// rfocusMorph函数校验blur、focus返回true cur=cur.parentNode; } for ( ; cur; cur=cur.parentNode ){ eventPath.push(cur); tmp=cur; } if ( tmp===( elem.ownerDocument || document ) ){ eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // 执行回调函数,按条件执行浏览器原生的事件处理函数 i=0; while ( ( cur=eventPath[i++] ) && !event.isPropagationStopped() ){ event.type=i>1 ? bubbleType : special.bindType || type; // 获取绑定回调函数并执行,以cur作为上下文 handle=( dataPriv.get(cur,"events") || {} )[event.type] && dataPriv.get(cur,"handle"); if ( handle ){ handle.apply(cur,data); } // 获取并执行浏览器原生事件cur[ontype],该事件函数返回否值时,阻止浏览器默认事件执行 handle=ontype && cur[ontype]; if ( handle && handle.apply && acceptData(cur) ){ event.result=handle.apply(cur,data); if ( event.result===false ){ event.preventDefault(); } } } event.type=type; if ( !onlyHandlers && !event.isDefaultPrevented() ){ // special._default返回为真值时,不执行elem[type] // special._default为否则或其返回为否值时,执行elem[type] if ( ( !special._default || special._default.apply(eventPath.pop(),data)===false ) && acceptData(elem) ){ // 获取并执行浏览器原生事件elem[type],执行过程中缓存elem[ontype] if ( ontype && jQuery.isFunction(elem[type]) && !jQuery.isWindow(elem) ){ tmp=elem[ontype]; if ( tmp ){ elem[ontype]=null; } jQuery.event.triggered=type; elem[type](); jQuery.event.triggered=undefined; if ( tmp ){ elem[ontype]=tmp; } } } } return event.result; }, // focus、blur事件使用,文档节点挂载simulate函数,促使event.target获得或失去焦点 simulate:function(type,elem,event){ var e=jQuery.extend( new jQuery.Event(), event, { type:type, isSimulated:true } ); jQuery.event.trigger(e,null,elem); } }); jQuery.fn.extend({ // $ele.trigger方法冒泡执行每个元素的事件,冒泡通过构建eventPath父级元素集合实现 trigger:function(type,data){ return this.each(function(){ jQuery.event.trigger(type,data,this); }); }, // $ele.triggerHandler方法执行首个元素的事件,并且阻止向上冒泡事件触发 triggerHandler: function(type,data){ var elem=this[0]; if ( elem ){ return jQuery.event.trigger(type,data,elem,true); } } }); return jQuery; });
3、$ele.blur|focus:
define([ "../core", "../event", "./trigger" ],function(jQuery){ "use strict"; // $ele.change等快速构建浏览器事件方法的实现,有参数时绑定事件,无参数时触发事件 // 调用$ele.on、$ele.trigger方法实现 // contextmenu鼠标右键事件,返回false禁用浏览器原生鼠标右键事件 jQuery.each( ("blur focus focusin focusout resize scroll click dblclick "+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave "+ "change select submit keydown keypress keyup contextmenu").split(" "), function(i,name){ jQuery.fn[name]=function(data,fn){ return arguments.length>0 ? this.on(name,null,data,fn) : this.trigger(name); }; }); // $ele.hover方法只是绑定鼠标移入移出事件,不触发 // 单参数时移出绑定函数和移入绑定函数相同 jQuery.fn.extend({ hover:function(fnOver,fnOut){ return this.mouseenter(fnOver).mouseleave(fnOut || fnOver); } }); });
4、ajax:
define([ "../core", "../event" ],function(jQuery){ "use strict"; // Attach a bunch of functions for handling common AJAX events jQuery.each([ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ],function(i,type){ jQuery.fn[type]=function(fn){ return this.on(type,fn); }; }); });
5、其他
define([ "../core", "../data/var/dataPriv", "./support", "../event", "./trigger" ],function(jQuery,dataPriv,support){ "use strict"; // 通过原生语句向文档节点绑定函数,以模拟单纯得到或失去焦点、不向上冒泡的情形 if ( !support.focusin ){ jQuery.each( {focus:"focusin",blur:"focusout"}, function(orig,fix){ // 原生语句向文档节点绑定函数时,调用jQuery.event.simulate方法, // 触发jQuery.event.trigger方式单纯执行jQuery.event.sepcial.trigger方法,只是得到或失去焦点 // 触发jQuery.event.trigger方法时和event.target关联,event.target得到或失去焦点 var handler=function(event){ jQuery.event.simulate(fix,event.target,jQuery.event.fix(event)); }; jQuery.event.special[fix]={ // focus、blur事件添加的回调存储在elemData.events.focusin|focusout中 // focus、blur回调挂载在this.ownerDocument元素,触发时取出执行 // 挂载在elemData.events.focusin|focusout中的回调需要focusin、focusout事件取出,冒泡 // focus、blur事件不获取执行elemData.events.focusin|focusout回调 // 向上冒泡时各元素都没挂载focus、blur事件,除this.ownerDocument文档元素执行事件 // 利用handler函数调用event.simulate方法,以event.trigger方式调用sepcial.trigger得到、失去焦点 setup:function(){ var doc=this.ownerDocument || this, attaches=dataPriv.access(doc,fix);// 只记录文档节点绑定focus、blur事件的数目,以便更新 if ( !attaches ){ doc.addEventListener(orig,handler,true); } dataPriv.access(doc,fix,(attaches || 0)+1); }, // 更新attaches,attaches为空时文档节点解绑handler函数 teardown: function(){ var doc=this.ownerDocument || this, attaches=dataPriv.access(doc,fix)-1; if ( !attaches ){// 文档节点不再绑定focus、blur事件 doc.removeEventListener(orig,handler,true); dataPriv.remove(doc,fix); } else { dataPriv.access(doc,fix,attaches); } } }; }); } return jQuery; });
define( [ "../var/support" ], function( support ) { "use strict"; support.focusin = "onfocusin" in window; return support; } );