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);
时间: 2024-12-21 04:44:53

jQuery的事件模型的相关文章

jquery事件模型

大多数工具类都会提供标准入口和快捷方式,jquery事件模型也不例外 jquery1.0标准入口 属于jquery工具方法,通过$.event可查看 一个标准的事件大致就会经历这样的流程 如果对事件的传播进一步进行代理就还需要一个fix,重新制作代理以及取消默认行为 jquery1.0快捷入口 通过初始化方式,将标准工具方法,转换为实例方法而后 再一次产生更加快捷的方法,至此,通过jquery实例方法有2中调用方式 1.$().bind(type,fn) 2.$().onclick(fn) 在1

DOM事件模型学习笔记

下面的内容属于阅读笔记,其中涉及大量内容来自于PPK的博客的内容,如果你要跟随我的脚步领略大家风采,那么就从Introduction to Events开始阅读吧. 现代的前端开发应该是会严格遵守 html 展示文档内容构成,css 渲染页面效果,javascript 提供交互 浏览器提供交互行为可以追溯到Netscape公司在其第二个版本中支持javascript语言,随后其与微软之间的浏览器大战,以及w3c标准制定的落后,导致至今一直被诟病的浏览器兼容问题,而这些不兼容中关于DOM事件模型的

ExtJS框架基础:事件模型及其常用功能

前言 工作中用ExtJS有一段时间了,Ext丰富的UI组件大大的提高了开发B/S应用的效率.虽然近期工作中天天都用到ExtJS,但很少对ExtJS框架原理性的东西进行过深入学习,这两天花了些时间学习了下.我并不推荐大家去研究ExtJS框架的源码,虽然可以学习其中的思想和原理,但太浪费精力了,除非你要自己写框架. 对于ExtJS这种框架,非遇到"杂症"的时候我觉得也没必要去研究其源码和底层的原理,对其一些机制大致有个概念,懂得怎么用就行,这也是本篇博文的主要目的. Ext自己的事件机制

JS的事件模型

之前对事件模型还是比较清楚的,许多概念都清晰映射在脑海中.工作之后,一方面使用的 局限性,二是习惯于用框架中的各种事件监听方式,简单即方便,久而久之,事件的一些概念开 始淡出记忆中,就像我现在已经开始淡忘C语言的指针.麦克斯韦方程组.矩阵的变换.最小二乘 法等.知识就像五彩缤纷的鹅卵石铺垫在你前行的道路上,从简单到深刻,从深刻到领悟,一直 助你渐行渐远.回头看看事件模型呗. 一.事件简简介事件包括:鼠标事件键盘事件框架事件 onerror onresize onscroll等表单事件事件 onb

07-jquery事件模型

jquery事件模型: dom0级事件模型: 阻止冒泡:event.stopPropagation(); 阻止冒泡:event.cancelBubble = true; 只支持一个事件处理函数: dom2级事件模型: addEventListener(eventType) addEventListener(listener) addEventListener(useCapture):如果为false,只冒泡不捕获,如果为true,只捕获,不冒泡. attachEvent(eventName,ha

JavaScript和jQuery的事件

一.添加事件监听函数-JavaScript 1.元素标签里直接写js代码 <body onload="var i=1; alert(i);"> </body> 2.标签里写函数名 <head> <script> function hi(){ alert("hi"); } </script> </head> <body onload="hi();"> </bo

Javascript事件模型系列(一)事件及事件的三种模型

转载: http://www.cnblogs.com/lvdabao/p/3265870.html 一.开篇 在学习javascript之初,就在网上看过不少介绍javascript事件的文章,毕竟是js基础中的基础,文章零零散散有不少,但遗憾的是没有看到比较全面的系列文章.犹记得去年这个时候,参加百度的实习生面试,被问到事件模型,当时被问的一头雾水,平时敲onclick敲的挺爽,却没有关注到事件模型的整体概念.这个周末难得清闲,决定就javascript中的事件模型写个系列,算是对知识点的一个

仅用原生JavaScript手写DOM事件模型

前言 前段时间博客园里有篇很火的帖子2016十家公司前端面试小记,主要讲作者的前端求职面试经历,其中提到了面试官会考察手写一个简单的事件模型: "如果上述都ok的话,那么极有可能要求让你[实现事件模型],即写一个类或是一个模块,有两个函数,一个bind一个trigger,分别实现绑定事件和触发事件,核心需求就是可以对某一个事件名称绑定多个事件响应函数,然后触发这个事件名称时,依次按绑定顺序触发相应的响应函数." 如果了解观察者模式,那么事件模型应该不算太难.本着深入钻研的精神,我试着来

JQuery实践--事件

通过HTML网页所呈现的界面是异步的和事件驱动的.步骤: 建立用户界面 等待又去的事情发生 做出相应的反应 重复 浏览器所实现的事件模型 DOM第0级事件模型 事件处理程序是通过吧函数实例的引用指派到DOM元素的属性而声明的.                                                  或HTML的特性.当指定到HTML特性上时,匿名函数就会利用特性的值作为函数体而自动创建onclick <=> click事件onmouseover <=>mo