jQuery源码学习6——工具方法之事件系统
事件系统结构如下
event:{ add:function(){},//添加一个事件 guid:1,//给事件触发函数绑定一个唯一的guid global:{},//global下每一项的键是一种事件类型,值是这种事件类型对应的方法 remove:function(){},//移除事件 trigger:function(){},//触发指定事件 handle:function(){},//add中调用,逻辑稍复杂 fix:function(){},//修正方法 },
(1)fix
fix: function(event) { if ( event ) { event.preventDefault = function() { this.returnValue = false; }; event.stopPropagation = function() { this.cancelBubble = true; }; } return event; }
这个方法在event事件对象上修正了preventDefault和stopPropagation两个方法
功能分别是阻止浏览器默认事件和阻止冒泡
(2)handle
handle: function(event) { if ( typeof jQuery == "undefined" ) return; event = event || jQuery.event.fix( window.event ); if ( !event ) return; var returnValue = true; var c = this.events[event.type]; for ( var j in c ) { if ( c[j].apply( this, [event] ) === false ) { event.preventDefault(); event.stopPropagation(); returnValue = false; } } return returnValue; },
这个方法中第一个if条件,搞不明白什么时候会进入
接下来event = event || jQuery.event.fix( window.event );
如果event存在的话,不做什么处理,event如果是null或者undefined的话就用jQuery.event.fix来修正
但是目前还是没有发现什么时候会走jQuery.event.fix
接下来这句有点绕,var c = this.events[event.type];
第一次看到this.events中的this,我认为它是event对象
但是问题来了,event对象下哪里有events这个属性呢?
后来看到在add方法中handle的调用时才发现:element["on" + type] = this.handle;
handle方法里面的this是element,element才有events属性!
所以这里的c拿到的就是当前事件类型所绑定的方法的集合
再往下的for循环便是依次遍历c里面的方法,然后挨个执行
for循环里面做了一个判断,就是当第j个方法要是返回false的话执行:
event.preventDefault();
event.stopPropagation();
即阻止默认事件和阻止冒泡
(3)、add
add: function(element, type, handler) { if ( jQuery.browser.msie && element.setInterval != undefined ) element = window; if ( !handler.guid ) handler.guid = this.guid++; if (!element.events) element.events = {}; var handlers = element.events[type]; if (!handlers) { handlers = element.events[type] = {}; if (element["on" + type]) handlers[0] = element["on" + type]; } handlers[handler.guid] = handler; element["on" + type] = this.handle; if (!this.global[type]) this.global[type] = []; this.global[type].push( element ); },
第一个if判断,从注释中看,貌似是说在IE浏览器下全局对象是有问题的
第二个if判断,如果要绑定的函数没有guid,就给它加上一个guid唯一标识
第三个if判断,如果element元素没有events属性就给其赋一个空对象
第四个if判断,如果element元素的events事件类型集合里面没有type类型的事件,就给其赋一个空对象
第四个if判断里面嵌套的if判断是查看有没有类似element.onclick这种情况
如果有的话将onclick绑定的函数添加到handlers的第0项当中,因为接下来马上就要将element.onclick绑定的函数覆盖了
接下来handlers[handler.guid] = handler;这句话就是将要绑定的handler函数加到handlers里面
接下来element["on" + type] = this.handle;就用this.handle这个方法将onxxx绑定的函数给覆盖了
而this.handle这个方法里面实际上把所有给这个元素绑定的这种类型的函数全都执行了
而this.handle这个函数就是在onxxx的时候触发的
在看最后一个if之前,先得了解一下this.global是什么
global这个对象里面的每一项的键都是一种事件类型,而所对应的值就是拥有这种事件的元素
最后这个if就是看global里面有没有刚才处理的这种事件
如果有的话,直接将元素push进去
没有的话先创建空数组,再push
global里面存储的对象会供trigger方法调用
(4)remove
remove: function(element, type, handler) { if (element.events) if (type && element.events[type]) if ( handler ) delete element.events[type][handler.guid]; else for ( var i in element.events[type] ) delete element.events[type][i]; else for ( var j in element.events ) this.remove( element, j ); },
顾名思义,remove就是移除事件
移除element上type类型里面绑定的handler事件
当然type和handler两个参数还可以不传
如果没有传type参数的话,不管传没传handler都会将elements上所有的事件全都移除
如果传入了type而且在element的events事件集合里面type类型的事件有定义
就再进一步判断有没有定义handler
如果定义了handler,那自然就删除对应的事件方法
否则的话删除element上type类型的事件
(5)、trigger
trigger: function(type,data,element) { // Touch up the incoming data data = data || []; // Handle a global trigger if ( !element ) { var g = this.global[type]; if ( g ) for ( var i = 0; i < g.length; i++ ) this.trigger( type, data, g[i] ); // Handle triggering a single element } else if ( element["on" + type] ) { // Pass along a fake event data.unshift( this.fix({ type: type, target: element }) ); // Trigger the event element["on" + type].apply( element, data ); } },
顾名思义,trigger就是触发事件
而这个触发指的是在程序里面手动触发,而不是click或者mouseover某个元素这种形式的触发
在触发自定义事件时经常使用
通过观察,在ajax模块里面用到trigger的地方很多
第一次看到trigger的时候搞不明白这个方法里面的data参数是什么意思
认真看了一遍以后发现这个参数是自定义的一个事件对象
什么意思呢?我们普通的事件触发函数,例如oDiv.onclick=function(e){}中的e就是onclick事件的事件对象
而对于我们自己定义的事件,是不会存在这样一个e对象的
这就要求我们自己定义一个事件对象
从方法里面第一行代码data = data || []来看,data貌似是一个形如数组的参数
如果传入了element参数,再判断element是否绑定了type类型的方法
else if里面的第一句就对data做了修正
接下来就触发了element上type类型的事件
并且传入data事件对象作为参数
如果没有传入element参数,这时global就发挥作用了
因为global的存储结构是按照事件类型存储,每种类型的值是绑定了这种事件的元素
因此接下来的工作就是得到绑定了type类型的事件的元素集合
然后遍历这个元素集合,挨个触发type事件
而且用自定义的data事件对象来触发