Event源码

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;

} );

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值