jQuery的事件模型
前几天自己着重读了jQuery1.11.1的源码,又结合了之前对DE事件模型的分析,最后也实现一个简陋的事件模型。
jQuery的事件系统离不开jQuery的缓存系统。
jQuery的第一代缓存是直接将数据存储在 缓存体 这个数据结构中,但是需要在
元素上添加一个uuid来作为标示,标记在缓存体中的位置。但是仔细想想,就会发现,如果对window或者document进行事件
侦听,就会在这两个对象上添加额外属性,会造成全局污染,不是很好。
所以jQuery第二代缓存系统应运而生,这次不对元素进行添加属性,而是判断元素的valueOf()方法的返回值,如果没有返回值是
对象,则说明缓存体中并没有该元素的缓存数据,进而使用ECMA5的Object.defineProperty来对valueOf方法进行重写,并返回
一个guid,作为访问缓存体的钥匙。
简单讲述了缓存系统,现在着重讲解下jQuery的事件系统:
主要使用了几个数据结构,即元素的缓存体,Event构造函数,和Handler构造函数。
当使用bind(el,type,fn)添加回调时,会根据Handler构造函数构造一个handler实例,在我的具体实现中,参数fn可能是一个函数,也可能
是一个对象,若是对象,则标记这个回调函数的功能--once函数或者throttle函数或delay函数。 其次就是对fn的封装,在库中,fn的包装函数
实现了新事件对象的创建,以及对新创建的事件对象的修补,并调整了在回调中this的指向。最后将该handlerObj存入该元素对应的缓存体中,
并用addEvent绑定事件。
使用unbind移除回调也比较简单,无非是移除缓存,移除回调。
trigger触发回调主要就是传入参数的处理,执行带有参数的回调。
现附上简单的实现:
(function(s){ var addEvent = (function(){ if(window.addEventListener){ return function (el,type,fn) { el.addEventListener(type,fn,false); } }else{ return function(el,type,fn){ el.attachEvent(‘on‘+type,fn); } } })(); var removeEvent = (function (){ if(window.removeEventListener){ return function(el,type,fn){ el.removeEventListener(type,fn,false); } }else{ return function(el,type,fn){ el.detachEvent(‘on‘+type,fn); } } })(); // HandlerObject constructor function Handler(config){ this.handler = config.handler; this.special = config.special; //特殊的回调,ex. once函数,throggle函数等等,原回调放在此处,handler放包裹后的回调 this.type = config.type; this.namespace = config.namespace; this.data = config.data; this.once = config.once; this.delay = config.delay; this.throttle = config.throttle; this.stop = config.stop; // 取消默认和冒泡 this.stopBubble = config.stopBubble; this.preventDefalut = config.preventDefalut; } //typeEvents=[handlerObj1,handlerObj2,...] function execHandlers(el,event,args,context){ // 若args不为空,则为自定义事件出发,trigger if(el.nodeValue == 3 || el.nodeValue == 8) return; var elData,events,handlers,typeEvents,ret, flag = true; context = context || el; //获取缓存对象 elData= S._data(el); if(!elData || !elData[‘events‘])return; events = elData[‘events‘]; handlers = elData[‘handlers‘]; if(!events[event.type])return; typeEvents = events[event.type]; for(var i= 0,len=typeEvents.length;i<len;i++){ ret = execHandler(el,event,typeEvents[i],args,context); if(ret == false){ flag = false } } if(!flag){ event.preventDefalut(); event.stopPropagation(); } return; } function execHandler(el,event,handlerObj,args,context){ var handler = handlerObj.handler, type = event.type, special = handlerObj.special, stop = handlerObj.stop, preventDefault = handlerObj.preventDefalut, stopBubble = handlerObj.stopBubble, data = handlerObj.data, once = handlerObj.once, delay = handlerObj.delay, // 时延 throttle = handlerObj.throttle; //最小间隔时间 if(handlerObj.type && type !== handlerObj.type) return; if(!handler || !S.isFunction(handler))return; if(stop){ event.preventDefalut(); event.stopPropagation(); } if(preventDefault){ event.preventDefalut(); } if(stopBubble){ event.stopPropagation(); } if(once){ var onceHandler = function(event,args){ return S.once(handler,context,event,args); }; return onceHandler.call(context,event,args); } if(delay && S.isNumber(delay)){ var delayHandler = function(event,args){ return S.delay(handler,context,delay,event,args); } return delayHandler.call(context,event,args); } if(throttle && S.isNumber(throttle)){ var throttleHandler = function(event,args){ return S.throttle(handler,context,throttle,event,args); } return throttleHandler.call(context,event,args); } if(handler){ return handler.call(context,event,args); } return; } function returnTrue(){ return true; } function returnFalse(){ return false; } //Event constructor function Event(e){ //传入事件参数 this.originalEvent = e; var type = e.type; if(/^(\w+)\.(\w+)$/.test(type)){ this.type = RegExp.$1; this.namespace = RegExp.$2 }else{ this.type = type; this.namespace = ‘‘; } } Event.prototype = { preventDefault: function(){ var e = this.originalEvent; if(e.preventDefalut){ return e.preventDefault(); } e.returnValue = false; this.isPreventDefault = returnTrue; return; }, stopPropagation: function(){ var e = this.originalEvent; if(e.stopPropagation){ return e.stopPropagation(); } e.stopBubble = true; this.isStopPropagation = returnTrue; return; }, stopImmediatePropagation: function(){ this.stopPropagation(); this.isImmediatePropagationStopped = returnTrue; }, isPreventDefault: returnFalse, isStopPropagation: returnFalse }; //事件修复 function fixEvent(event){ var i, prop, props = [], originalEvent = event.originalEvent; props = props.concat(‘altKey bubbles button cancelable charCode clientX clientY ctrlKey currentTarget‘.split(‘ ‘)); props = props.concat(‘data detail eventPhase fromElement handler keyCode layerX layerY metaKey‘.split(‘ ‘)); props = props.concat(‘newValue offsetX offsetY originalTarget pageX pageY prevValue relatedTarget‘.split(‘ ‘)); props = props.concat(‘screenX screenY shiftKey target toElement view wheelDelta which‘.split(‘ ‘)); for(i=props.length;--i;){ event[props[i]] = originalEvent[props[i]]; } if(!event.target){ event.target = event.srcElement; } if(event.target.nodeType == 3){ event.target = event.target.parentNode; } if(!event.relatedTarget){ event.relatedTarget = event.fromElement === event.target? event.toElement : event.fromElement; } if(!event.which && (event.charCode || event.keyCode)){ event.which = event.charCode ? event.charCode : event.keyCode ? event.keyCode : null; } if(!event.pageX || !event.pageY){ event.pageX = event.clientX + (doc.documentElement && doc.documentElement.scrollLeft || doc.body && doc.body.scrollLeft || 0) - (doc.documentElement && doc.documentElement.clientLeft || doc.body && doc.body.clientLeft || 0); event.pageY = event.clientY + (doc.documentElement && doc.documentElement.scrollTop || doc.body && doc.body.scrollTop || 0) - (doc.documentElement && doc.documentElement.clientTop || doc.body && doc.body.clientTop || 0); } if(!event.which && event.button != undefined){ //ie下 0 无动作, 1 左键 ,2 右键, 4 中间键 event.which = (event.button & 1) ? 1 : (event.button & 2) ? 3 : (event.button & 4) ? 2 : 0; } return event; } function bind(el,type,fn){ if(el.nodeType && el.nodeType == 3 || el.nodeType == 8) return; var elData= S._data(el),events,handlers,typeEvents; if(!elData) { S._lockData(el); //开辟缓存 elData = S._data(el); } if(!elData[‘events‘]){ elData[‘events‘] = {}; } events = elData[‘events‘]; handlers = elData[‘handlers‘]; // 目前先不对其赋值 if(!events[type]){ events[type] = []; } typeEvents = events[type]; var handlerObj; if(S.isFunction(fn)){ handlerObj = new Handler({handler: fn}); }else if(S.isObject(fn)){ handlerObj = new Handler(fn); }else{ return; } handlerObj.handlerHook = function(event,args){ // 函数钩子,用于unbind删除回调函数 event = event || window.event; var e = new Event(event); e = fixEvent(e); execHandlers(el,e,args,el); }; typeEvents.push(handlerObj); addEvent(el,type,handlerObj.handlerHook); } function unbind(el,type,fn){ if(el.nodeType && el.nodeType == 3 || el.nodeType == 8) return; var elData= S._data(el),events,handlers,typeEvents; if(!elData || !elData[‘events‘])return; if(arguments.length == 1){ // 删除该元素所有缓存 事件 for(var i in elData[‘events‘]){ if(elData[‘events‘].hasOwnProperty(i)){ for(var j=0,len=elData[‘events‘][i].length;j<len;j++){ removeEvent(el,i,elData[‘events‘][i][j].handlerHook); } } } S._unData(el); } events = elData[‘events‘][type]; if(arguments.length == 2 && events){ try{ for(var i= 0,len=events.length;i<len;i++){ removeEvent(el,type,events[i].handlerHook); } }catch(e){ throw new TypeError(‘哎呀啊,解除回调出现意外‘) } events = {}; delete elData[type]; } if(arguments.length == 3){ for(var i= 0,len=events.length;i<len;i++){ if(events[i].handler === fn){ try{ removeEvent(el,type,events[i].handlerHook); }catch(e){ throw new TypeError(‘哎呀啊,解除回调出现意外‘) } events.splice(i,1); } } } } function trigger(el,type,args){ if(el.nodeType && el.nodeType == 3 || el.nodeType == 8) return; var elData= S._data(el),events,handlers,typeEvents; if(!elData || !elData[‘events‘] || !elData[‘events‘][type])return; events = elData[‘events‘][type]; var handlerObj,event; event = { target: el, type: type, data: args }; for(var len=events.length;--len>=0;){ handlerObj = events[len]; handlerObj.handlerHook(event,args); } } S.fn.extend({ bind: function(type,fn){ this.each(function(el){ bind.call(el,el,type,fn); }) }, unbind: function(type,fn){ var args = arguments; this.each(function(el){ args.length == 0 ? unbind.call(el,el) : args.length == 1 ? unbind.call(el,el,type) : unbind.call(el,el,type,fn); }) }, trigger: function(type,args){ this.each(function(el){ trigger.call(el,el,type,args); }) } }) })(Screen);
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。