首页 > 代码库 > jQuery的事件模型

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 = http://www.mamicode.com/config.data;> 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);

 

jQuery的事件模型