上一章对于jQuery的事件系统,对于jQuery的一些事件绑定接口作了分析。同时,引入了事件委托的概念。那么,从本章起,将开始深刻到jQuery的事件系统内部,对于其源码进行解析。html
这一篇是能够独自拿出来看,与前面两章虽然有些关系,可是若是只是对于jQuery源码有兴趣的,而且对前端事件有些理解的,从这章开始看也是能够的。前端
前端事件系统(一)jquery
前端事件系统(二)api
上一章说起到jQuery绑定的核心,是落在了on方法身上的。on方法至关于对于jQuery的事件接口进行了统一,让jQuery其余事件绑定有关的方法,直接调用on方法来进行实现。所以,咱们将拿on方法做为jQuery事件一开始的一个突破口,来对jQuery事件进行层层分析。下面来看jQuery的on方法数组
on : function( types, selector, data, fn, /*INTERNAL*/ one ) { var origFn, type; // Types can be a map of types/handlers /* * 第一个参数传入为对象的处理,相似 * {“event1name”:function(){}, * "event2name":function(){} * } * 这样的写法。 * 而后调用自身on方法,分别对其进行on方法绑定事件 */ if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // ( types-Object, data ) data = data || selector; selector = undefined; } for ( type in types ) { this.on( type, selector, data, types[ type ], one ); } return this; } //对于data与fn为null的状况下的处理 if ( data == null && fn == null ) { // ( types, fn ) fn = selector; data = selector = undefined; //对于仅仅fn为null的状况下的处理 } else if ( fn == null ) { if ( typeof selector === "string" ) { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } //若是执行函数直接是false的状况的处理returnFalse便是直接return False的函数 if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { return this; } //若是最后一个参数为1,那么直接调用fn,并解绑。 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++ ); } //直接调用jQuery.event.add并传入处理好的参数 return this.each( function() { jQuery.event.add( this, types, fn, data, selector ); }); }
能够发现,做为绑定核心的on方法,其实质上只是对传进来的参数进行调整处理,并将处理事后的参数,传入jQuery.event.add中来进行处理,所以,接下来咱们将对jQuery.event.add的源码来进行分析与阅读。浏览器
这个方法比较长,而且内部有许多东西,暂时还很差去读,所以,我将这一部分分为几个部分来进行阅读。为了方便理解,我将其分红几个部分,而后一一来解读。我尽可能作到对这一部分的讲解作到简单易懂,解读过程当中有不对的地方,还请指出。同时,这一部分的代码,除非特别说明,不然所有来自于jQuery.event.add。缓存
首先,咱们先直接跳到事件注册那一部分,即addeventListener,看看他作了些什么app
// Init the event handler queue if we're the first // 若是是第一次,那么初始化事件队列 if ( !(handlers = events[ type ]) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener if the special events handler returns false // 若是获取特殊事件失败,那么采用addEventListener来进行事件注册 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { if ( elem.addEventListener ) { // 注册时将eventHandle来进行注册,而且仅仅执行一次 elem.addEventListener( type, eventHandle, false ); } } }
这段代码,实际就是对于handlers的初始化,同时,对事件采用addEventListenr进行了绑定。dom
在这一段代码中,咱们能够看出,jQuery在绑定事件时,与如下几个部分有关:函数
下面将对这几个部分,以及这几个部分衍生出来的部分一一进行解读。同时,如下几部分代码,为了方便阅读,对自己这部分的代码进行了一些抽象(这词用到这里怎么怪怪的),方便于理解
//获取数据缓存 var elemData = data_priv.get( elem ); ... // 若是elemData没有handle属性,给其初始化一个handle属性 if ( !(eventHandle = elemData.handle) ) { eventHandle = elemData.handle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; }
这里首先对于咱们要操做的结点下的数据缓存进行了获取,同时,将数据缓存下的handle属性,赋给了eventHandle。这里能够发现,elemData.handle这一部分,并无直接对于事件的回调函数进行处理,而是进一步的使用jQuery.event.dispatch来进行的处理。
jQuery.event.dispatch这里还不会进行阅读,咱们只要知道,jQuery对于回调的处理,和jQuery.event.dispatch这部分有关就好了。
var //获取数据缓存 elemData = data_priv.get( elem ), events; ... // Init the element's event structure and main handler, if this is the first // 若是elemData没有events属性,给其初始化一个events属性 if ( !(events = elemData.events) ) { events = elemData.events = {}; }
events部分,来自于要操做节点下的数据缓存中的events属性。而根据以前的操做
// Init the event handler queue if we're the first // 若是是第一次,那么初始化事件队列 if ( !(handlers = events[ type ]) ) { handlers = events[ type ] = []; ... }
能够明白,events便是对于操做节点下各类事件的类型进行的记录。
var handlers; ... // Init the event handler queue if we're the first // 若是是第一次,那么初始化事件队列 if ( !(handlers = events[ type ]) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; ... } ... // Add to the element's handler list, delegates in front // 将事件处理的对象加入处理列表 if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); }
handlers实际上是做为一个列表的做用,在里面存放了handleObj的有关内容。
具体存入了什么,咱们接下来来看handleObj具体是什么。
var handleObjIn,handleObj; ... // Caller can pass in an object of custom data in lieu of the handler // 对于像{handler:function(){},...}这样的处理(即就是下面的handleObj的格式),后面可用于对handleObj的扩展 if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } ... while ( t-- ) { ... // handleObj is passed to all event handlers // handleObj是做为对事件处理的对象 // 扩展handleObj的属性 handleObj = jQuery.extend({ type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join(".") }, handleObjIn ); ... // Add to the element's handler list, delegates in front // 将事件处理的对象加入处理列表 if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } ... }
handleObj即就是对于事件处理的一个对象,其包含了事件类型,初始类型,事件id,命名空间,dom元素,回调函数等属性。
因此回到以前的handlers,其放入的内容,也就是事件处理对象。
那么到了这里,咱们对于jQuery.event.add方法已经有了一些头绪了。那么咱们来整理下到目前为止的流程
到这里,add部分作了些什么,咱们大概都清楚了。
再回到以前的代码上,咱们能够看到,其中的一部分是被while循环所包裹的,那这一部分是实现的什么样的内容呢?
// Handle multiple events separated by a space // rnotwhite = (/\S+/g) // 将空格分开的types变为数组 types = ( types || "" ).match( rnotwhite ) || [ "" ]; // 获取事件类型的长度 t = types.length; while ( t-- ) { //rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; //对事件命名空间进行分割与获取 //例:click.myPlugin.simple tmp = rtypenamespace.exec( types[t] ) || []; //type则为click type = origType = tmp[1]; //namespaces则为['myPlugin','simple'] namespaces = ( tmp[2] || "" ).split( "." ).sort(); ... }
这里,对于多事件(相似于("mouseover click"))进行了处理,而且经过while循环分别对其进行事件注册。同时,while循环中对于事件的命名空间也进行了处理。
其实以前已经将这个列举了出来,不过由于其并不影响以前的对于整个的阅读,所以,将其拿到最后来讲明。
var special; ... while( t-- ) { ... // If event changes its type, use the special event handlers for the changed type // 若是事件改变了其type,采用特殊事件处理 special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type // 若是selector定义了,就采用special事件的type处理,不然直接采用type type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type // 前面type重写了,所以对special进行一次update special = jQuery.event.special[ type ] || {}; ... // 特殊事件使用add对其进行处理 if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } ... }
由于浏览器兼容性的问题,对于一些事件并不能兼容。所以,对于这些事件,jQuery采用了special event的机制来对一些特殊的事件来进行处理,以达到跨浏览器支持事件的功能。
距离上次写第二篇到如今也很久了,人仍是太懒了,写的也很慢。这一章对于on与event.add方法进行了解读,下一章将对于这一章所没讲到的事件委派来进行解析,嗯,仍是由于人懒,时间未知。