zoukankan      html  css  js  c++  java
  • 【原创】jQuery1.8.2源码解析之jQuery.event

    本片随笔主要是分析了下jQuery的事件模型,即如何统一事件对象,以及处理过程。

    这里简要说明一下几点:

    jQuery通过统一的方法(第62行),eventHandle函数进行事件的分发,利用jQuery.Event统一修正了浏览器的event对象,注册事件处理器时,也是注册eventHandle,

    然后统一将相关的事件信息,存储在与dom相联系的jQuery.cache缓存中,如下图:

    值得注意的还有事件代理和trigger的实现:

    (1)事件代理是个很不错的想法,利用了事件冒泡,在父元素上绑定事件,jQuery通过selector与事件处理器handle的selector进行匹配,从而达到代理的作用,

      另外,一个元素绑定的事件处理器分为两种:自身绑定的事件处理器 和 子元素绑定的代理事件处理器,显然为了体现冒泡(子元素可能阻止冒泡),当该元素上的某个事件触发时

      它的代理事件处理器是要先执行,之后再执行自身的事件处理器。对于live事件而言就是事件代理,与delegate不同的是,它默认是绑定在document上了(如果没有指定context的话)。

    (2)trigger主动触发事件,jQuery在trigger上同样很好的实现了事件冒泡,还有元素默认操作

    最后:本文主要是介绍和分析原理,对代码主干进行源码解析,对于一些IE兼容性的处理,没有做过多分析,下面是源码分析(中英文注释):

       1 var rformElems = /^(?:textarea|input|select)$/i,
       2     rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/,
       3     rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
       4     rkeyEvent = /^key/,
       5     rmouseEvent = /^(?:mouse|contextmenu)|click/,
       6     rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
       7     hoverHack = function( events ) {
       8         return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
       9     };
      10 
      11 /*
      12  * Helper functions for managing events -- not part of the public interface.
      13  * Props to Dean Edwards' addEvent library for many of the ideas.
      14  */
      15 jQuery.event = {
      16 
      17     add: function( elem, types, handler, data, selector ) {
      18 
      19         var elemData, eventHandle, events,
      20             t, tns, type, namespaces, handleObj,
      21             handleObjIn, handlers, special;
      22 
      23         // Don't attach events to noData or text/comment nodes (allow plain objects tho)
      24         // 以下条件满足一个则退出事件绑定:
      25         // 是文本节点和注释节点
      26         // types为空
      27         // hander为空
      28         // 内部缓存数据(pvt为true)
      29         if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
      30             return;
      31         }
      32 
      33         // Caller can pass in an object of custom data in lieu of the handler
      34         // 调用这可以传递一个自定义数据来代替handler(事件处理器)
      35         // 自定义数据类似:
      36         // {
      37         //     handler : fn
      38         //     ,selector : '...'
      39         // }
      40         if ( handler.handler ) {
      41             handleObjIn = handler;
      42             handler = handleObjIn.handler;
      43             selector = handleObjIn.selector;
      44         }
      45 
      46         // Make sure that the handler has a unique ID, used to find/remove it later
      47         // 如果改事件处理器没有guid(没有被添加过),那么给它添加guid,便于之后的查找和删除
      48         // jQuery.guid从1开始计算
      49         if ( !handler.guid ) {
      50             handler.guid = jQuery.guid++;
      51         }
      52 
      53         // Init the element's event structure and main handler, if this is the first
      54         // 初始化内部数据events
      55         events = elemData.events;
      56         if ( !events ) {
      57             elemData.events = events = {};
      58         }
      59         // 初始化内部数据handle,handle将成为所有事件触发时的处理函数
      60         eventHandle = elemData.handle;
      61         if ( !eventHandle ) {
      62             elemData.handle = eventHandle = function( e ) {
      63                 // Discard the second event of a jQuery.event.trigger() and
      64                 // when an event is called after a page has unloaded
      65                 return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
      66                     jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
      67                     undefined;
      68             };
      69             // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
      70             // 将elem作为eventHandle的属性存储,用来避免IE中非本地事件的内存泄漏  
      71             eventHandle.elem = elem;
      72         }
      73 
      74         // Handle multiple events separated by a space
      75         // jQuery(...).bind("mouseover mouseout", fn);
      76         // hoverHack(...)将hover事件解释成mouseenter和mouseleave两个事件
      77         // 去除字符串前后的的空格,并拆分成事件类型数组
      78         types = jQuery.trim( hoverHack(types) ).split( " " );
      79         for ( t = 0; t < types.length; t++ ) {
      80 
      81             tns = rtypenamespace.exec( types[t] ) || [];
      82             // 事件类型
      83             type = tns[1];
      84             // 命名空间(值可能为undefined)
      85             // 多级命名空间(其实没所谓的多级),排序
      86             namespaces = ( tns[2] || "" ).split( "." ).sort();
      87 
      88             // If event changes its type, use the special event handlers for the changed type
      89             // 特殊的事件需要进行特殊处理,比如focus,blur等
      90             special = jQuery.event.special[ type ] || {};
      91 
      92             // If selector defined, determine special event api type, otherwise given type
      93             // 如果selector有值,则表示适用代理
      94             type = ( selector ? special.delegateType : special.bindType ) || type;
      95 
      96             // Update special based on newly reset type
      97             special = jQuery.event.special[ type ] || {};
      98 
      99             // handleObj is passed to all event handlers
     100             // 每一个事件处理器都对应存储着一个handleObj
     101             // type与oriType肯能不同,比如oriType为mouseenter,type为mouseover
     102             // 这里extend了handleObjIn,这意味着使用者可以在传递进来的handle里大做文章,存储自定义的数据。
     103             handleObj = jQuery.extend({
     104                 type: type,                             // fix后的事件类型
     105                 origType: tns[1],                       // 原始的事件类型 
     106                 data: data,                             // 传递进来的数据
     107                 handler: handler,                       // 事件处理器
     108                 guid: handler.guid,                     // 事件处理器的唯一id
     109                 selector: selector,                     // 代理事件处理器需要的选择器,用来过滤
     110                 namespace: namespaces.join(".")         //命名空间(经过排序)
     111             }, handleObjIn );
     112 
     113             // Init the event handler queue if we're the first
     114             // 获取type事件下的所有handler(array)
     115             handlers = events[ type ];
     116             // 如果为空,进行第一次初始化
     117             if ( !handlers ) {
     118                 handlers = events[ type ] = [];
     119                 // handlers中作为代理的个数
     120                 handlers.delegateCount = 0;
     121 
     122                 // Only use addEventListener/attachEvent if the special events handler returns false
     123                 // 特殊事件需要用setup进行特殊处理,如:focusin,focusout等(后面有处理)
     124                 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
     125                     // Bind the global event handler to the element
     126                     // 将事件统一绑定到eventHandle,统一接口。
     127                     // 采用冒泡,与ie兼容
     128                     if ( elem.addEventListener ) {
     129                         elem.addEventListener( type, eventHandle, false );
     130 
     131                     } else if ( elem.attachEvent ) {
     132                         elem.attachEvent( "on" + type, eventHandle );
     133                     }
     134                 }
     135             }
     136 
     137             if ( special.add ) {
     138                 special.add.call( elem, handleObj );
     139 
     140                 if ( !handleObj.handler.guid ) {
     141                     handleObj.handler.guid = handler.guid;
     142                 }
     143             }
     144 
     145             // Add to the element's handler list, delegates in front
     146             if ( selector ) {
     147                 // 将事件代理处理器添加到handlers的最前面,方便之后代理处理器优先执行,用的很巧妙
     148                 handlers.splice( handlers.delegateCount++, 0, handleObj );
     149             } else {
     150                 // 普通事件处理器添加到依次push到handler list中
     151                 handlers.push( handleObj );
     152             }
     153 
     154             // Keep track of which events have ever been used, for event optimization
     155             // 记录哪些事件曾将使用过,为了事件的优化
     156             jQuery.event.global[ type ] = true;
     157         }
     158 
     159         // Nullify elem to prevent memory leaks in IE
     160         // 去除elem的引用,防止ie内存泄露
     161         elem = null;
     162     },
     163 
     164     global: {},
     165 
     166     // Detach an event or set of events from an element
     167     remove: function( elem, types, handler, selector, mappedTypes ) {
     168 
     169         var t, tns, type, origType, namespaces, origCount,
     170             j, events, special, eventType, handleObj,
     171             elemData = jQuery.hasData( elem ) && jQuery._data( elem );
     172 
     173         if ( !elemData || !(events = elemData.events) ) {
     174             return;
     175         }
     176 
     177         // Once for each type.namespace in types; type may be omitted
     178         types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
     179         for ( t = 0; t < types.length; t++ ) {
     180             tns = rtypenamespace.exec( types[t] ) || [];
     181             type = origType = tns[1];
     182             namespaces = tns[2];
     183 
     184             // Unbind all events (on this namespace, if provided) for the element
     185             // 如果types为'.mynamespace.hello'时,type此时就为空,那么删除该命名空间下的所有事件
     186             if ( !type ) {
     187                 for ( type in events ) {
     188                     jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
     189                 }
     190                 continue;
     191             }
     192 
     193             special = jQuery.event.special[ type ] || {};
     194             // 修正type
     195             type = ( selector? special.delegateType : special.bindType ) || type;
     196             // 该type(事件类型)下的所有函数处理器handleObj(array)
     197             eventType = events[ type ] || [];
     198             origCount = eventType.length;
     199             // 动态生成正则表达式,匹配命名空间
     200             namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
     201 
     202             // Remove matching events
     203             // 遍历handleObj list查找匹配的handler,并执行删除操作
     204             // mappedTypes 暂时不知道何用,应该是内部控制吧
     205             for ( j = 0; j < eventType.length; j++ ) {
     206                 handleObj = eventType[ j ];
     207 
     208                 // 一系列的逻辑,表示的基本意思如下:
     209                 // 1. origType === handleObj.origTypes是为了处理jQuery会将mouseenter修正为mouseover这种情况,因为此时mouseenter和mouseover的handleObjs
     210                 //    会处于同一个集合(对象)里,而它的区别就在于handleObj.oriType,这里做判断,保证能够正确删除mouseenter或者mouseover事件下的事件处理器
     211                 // 2. 如果handler不存在,那么就当作是删除该事件下的所有函数处理器,如果存在,则要handler.guid === handleObj.guid保证删除指定事件
     212                 //    处理器,因为handler和handleObj是通过guid对应的(一对多的关系,即一个事件处理器可以被同一个事件注册和调用多次)。
     213                 // 3. 如果命名空间不存在,那么就忽略命名空间,否则需要进行匹配,这里如果命名空间为多级,只需要其中的一级或多级组合便可以指定其,并删除。
     214                 //    如:如果注册click事件,命名空间是'aa.bb.cc',处理器为fn1,那么'click.aa','click.aa.cc'都是可以用来指定并删除fn1的(所以说是多级是不合理的)。
     215                 // 4. 如果selector不存在,那么忽略selector,否则如果存在则寻找selector === handleObj.selector的事件处理器进行删除,存在一个特殊值'**',
     216                 //    如果selector为'**',那么删除所有存在selector的handleObj
     217                 if ( ( mappedTypes || origType === handleObj.origType ) &&
     218                      ( !handler || handler.guid === handleObj.guid ) &&
     219                      ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
     220                      ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
     221                     //这里的设计比较好,保整handleObj的正常删除
     222                     eventType.splice( j--, 1 );
     223 
     224                     if ( handleObj.selector ) {
     225                         // 事件代理处理器个数减减
     226                         eventType.delegateCount--;
     227                     }
     228                     if ( special.remove ) {
     229                         special.remove.call( elem, handleObj );
     230                     }
     231                 }
     232             }
     233 
     234             // Remove generic event handler if we removed something and no more handlers exist
     235             // (avoids potential for endless recursion during removal of special event handlers)
     236             // eventType如果从有到无,那么进行一系列的清除工作,special事件仍然做自己的特殊处理
     237             if ( eventType.length === 0 && origCount !== eventType.length ) {
     238                 if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
     239                     jQuery.removeEvent( elem, type, elemData.handle );
     240                 }
     241                 // 删除缓存中events[type]
     242                 delete events[ type ];
     243             }
     244         }
     245 
     246         // Remove the expando if it's no longer used
     247         // 如果不存在任何事件处理器,则去除elemData.handle(所有事件的统一事件处理器)
     248         if ( jQuery.isEmptyObject( events ) ) {
     249             delete elemData.handle;
     250 
     251             // removeData also checks for emptiness and clears the expando if empty
     252             // so use it instead of delete
     253             // 用jQuery.removeData删除events,是为了做好清理工作(包括dom上的expando属性或者普通js对象的expando对象,以及缓存在jQuery.cahe的数据)
     254             jQuery.removeData( elem, "events", true );
     255         }
     256     },
     257 
     258     // Events that are safe to short-circuit if no handlers are attached.
     259     // Native DOM events should not be added, they may have inline handlers.
     260     customEvent: {
     261         "getData": true,
     262         "setData": true,
     263         "changeData": true
     264     },
     265 
     266     // onlyHandlers在调用triggerHandler时使用
     267     trigger: function( event, data, elem, onlyHandlers ) {
     268         // Don't do events on text and comment nodes
     269         // 不处理文本节点和注释节点
     270         if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
     271             return;
     272         }
     273 
     274         // Event object or event type
     275         var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,
     276 
     277             // 兼容jQuery.Event(object) 和 event(string)
     278             type = event.type || event,
     279             namespaces = [];
     280 
     281         // focus/blur morphs to focusin/out; ensure we're not firing them right now
     282         if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
     283             return;
     284         }
     285 
     286         if ( type.indexOf( "!" ) >= 0 ) {
     287             // Exclusive events trigger only for the exact event (no namespaces)
     288             type = type.slice(0, -1);
     289             exclusive = true;
     290         }
     291         // 解析命名空间
     292         if ( type.indexOf( "." ) >= 0 ) {
     293             // Namespaced trigger; create a regexp to match event type in handle()
     294             namespaces = type.split(".");
     295             type = namespaces.shift();
     296             namespaces.sort();
     297         }
     298 
     299         if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
     300             // No jQuery handlers for this event type, and it can't have inline handlers
     301             return;
     302         }
     303 
     304         // Caller can pass in an Event, Object, or just an event type string
     305         event = typeof event === "object" ?
     306             // jQuery.Event object
     307             // jQuery.Event或者修正过的event对象
     308             event[ jQuery.expando ] ? event :
     309             // Object literal
     310             // 字面对象,如{type:'click',...}
     311             new jQuery.Event( type, event ) :
     312             // Just the event type (string)
     313             // event(string),如:'click',则创建jQuery.Event对象
     314             new jQuery.Event( type );
     315 
     316         event.type = type;
     317         event.isTrigger = true;
     318         event.exclusive = exclusive;
     319         event.namespace = namespaces.join( "." );
     320         event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
     321         ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
     322 
     323         // Handle a global trigger
     324         if ( !elem ) {
     325 
     326             // TODO: Stop taunting the data cache; remove global events and always attach to document
     327             cache = jQuery.cache;
     328             for ( i in cache ) {
     329                 if ( cache[ i ].events && cache[ i ].events[ type ] ) {
     330                     jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
     331                 }
     332             }
     333             return;
     334         }
     335 
     336         // Clean up the event in case it is being reused
     337         // 清理event,防止该event正在使用中,有残留历史
     338         event.result = undefined;
     339         if ( !event.target ) {
     340             event.target = elem;
     341         }
     342 
     343         // Clone any incoming data and prepend the event, creating the handler arg list
     344         // 将data转换为数组
     345         data = data != null ? jQuery.makeArray( data ) : [];
     346         // 将事件对象插入数组最前面(开始位置),最好将这个数组作为参数传递给事件处理函数
     347         data.unshift( event );
     348 
     349         // Allow special events to draw outside the lines
     350         special = jQuery.event.special[ type ] || {};
     351         if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
     352             return;
     353         }
     354 
     355         // Determine event propagation path in advance, per W3C events spec (#9951)
     356         // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
     357         // 用eventPath存储冒泡路径[[elem, 'click'], [elemParent, 'click'],...,[window,'click']]
     358         eventPath = [[ elem, special.bindType || type ]];
     359         if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
     360 
     361             bubbleType = special.delegateType || type;
     362             cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
     363             for ( old = elem; cur; cur = cur.parentNode ) {
     364                 eventPath.push([ cur, bubbleType ]);
     365                 old = cur;
     366             }
     367 
     368             // Only add window if we got to document (e.g., not plain obj or detached DOM)
     369             // 冒泡是一直冒泡到window对象
     370             if ( old === (elem.ownerDocument || document) ) {
     371                 eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
     372             }
     373         }
     374 
     375         // Fire handlers on the event path
     376         // 依次冒泡调用指定type的事件吃利器
     377         for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
     378 
     379             cur = eventPath[i][0];
     380             event.type = eventPath[i][1];
     381             // 查询每个元素type类型的事件处理器
     382             handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
     383             if ( handle ) {
     384                 // 这里其实很重要,在进行冒泡的时候,传递的是同一个event对象(在data中)
     385                 // 也就是在这里便可以通过event对象或者return false,随时停止冒泡
     386                 handle.apply( cur, data );
     387             }
     388             // Note that this is a bare JS function and not a jQuery handler
     389             // 处理通过onType属性添加的事件处理器(如:elem.onClick = function(){...};)
     390             // 这里就可以知道trigger的时候onType事件是在通过jquery添加的事件后面执行的
     391             handle = ontype && cur[ ontype ];
     392             if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
     393                 // 注意这里onType事件处理器的return false只有可能阻止默认行为
     394                 event.preventDefault();
     395             }
     396         }
     397         // 修正type,防止事件处理器改变event.type的值
     398         event.type = type;
     399 
     400         // If nobody prevented the default action, do it now
     401         // 这里主要是用来执行元素的默认操作
     402         if ( !onlyHandlers && !event.isDefaultPrevented() ) {
     403 
     404             if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
     405                 !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
     406 
     407                 // Call a native DOM method on the target with the same name name as the event.
     408                 // Can't use an .isFunction() check here because IE6/7 fails that test.
     409                 // Don't do default actions on window, that's where global variables be (#6170)
     410                 // IE<9 dies on focus/blur to hidden element (#1486)
     411                 if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
     412 
     413                     // Don't re-trigger an onFOO event when we call its FOO() method
     414                     // 假设type为click
     415                     // 因为下面想通过click()来触发默认操作,但是又不想执行对应的事件处理器(re-trigger),
     416                     // 所以需要做两方面工作:
     417                     // 首先将elem.onclick = null;
     418                     // 然后将jQuery.event.triggered = 'click'; 将在入口handle(第62行)不再dispatch了
     419                     // 之后再将它们还原
     420                     old = elem[ ontype ];
     421 
     422                     if ( old ) {
     423                         // 暂时去除事件处理器
     424                         elem[ ontype ] = null;
     425                     }
     426 
     427                     // Prevent re-triggering of the same event, since we already bubbled it above
     428                     // 相当于是标记,表示事件处理器已经调用过了
     429                     jQuery.event.triggered = type;
     430                     // 再次调用,如elem.click(),(即trigge click)再次冒泡,不过这次执行入口handle,不会执行dispatch之后的代码了,只为触发默认操作
     431                     elem[ type ]();
     432                     jQuery.event.triggered = undefined;
     433 
     434                     if ( old ) {
     435                         elem[ ontype ] = old;
     436                     }
     437                 }
     438             }
     439         }
     440 
     441         return event.result;
     442     },
     443 
     444     dispatch: function( event ) {
     445 
     446         // Make a writable jQuery.Event from the native event object
     447         // event || window.event 兼容标准事件模型和IE事件模型
     448         // fix 修正event,返回jQuery.Event对象
     449         event = jQuery.event.fix( event || window.event );
     450 
     451         var i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related,
     452             // handle数组
     453             handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
     454             // handle中代理的个数
     455             delegateCount = handlers.delegateCount,
     456             // 浏览器传来的参数,伪数组转换为真正的数组
     457             // args[0]为原生的event对象
     458             args = [].slice.call( arguments ),
     459             run_all = !event.exclusive && !event.namespace,
     460             special = jQuery.event.special[ event.type ] || {},
     461             handlerQueue = [];
     462 
     463         // Use the fix-ed jQuery.Event rather than the (read-only) native event
     464         // 修正args[0]。
     465         // 因为在IE下,传入的参数event为undefined,就意味着arguments.length为0,改变event并不会影响arguments,所以arguments仍然为undefined
     466         args[0] = event;
     467         // 事件代理元素
     468         event.delegateTarget = this;
     469 
     470         // Call the preDispatch hook for the mapped type, and let it bail if desired
     471         if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
     472             return;
     473         }
     474 
     475         // Determine handlers that should run if there are delegated events
     476         // 对于代理处理器,需要进行一定过滤,决定哪些代理处理器可以运行
     477         // Avoid non-left-click bubbling in Firefox (#3861)
     478         // 火狐浏览器右键或者中键点击时,会错误地冒泡到document的click事件,并且stopPropagation也无效
     479         // 故这样的代理处理器需要避免
     480         // 如果存在代理处理器(若是click事件,要求是左键触发的),那么才进行事件代理
     481         if ( delegateCount && !(event.button && event.type === "click") ) {
     482 
     483             // Pregenerate a single jQuery object for reuse with .is()
     484             // 生成一个jQuery对象,这里的this没有任何用处,后面会将其dom元素覆盖,只为之后重复调用is()方法,
     485             // 而不必生成新的jQuery对象
     486             jqcur = jQuery(this);
     487             jqcur.context = this;
     488 
     489             // 从触发事件的元素开始,一直向根节点搜索匹配selector的元素,并执行事件处理函数
     490             for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
     491 
     492                 // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #xxxx)
     493                 // 不处理元素为disabled的click事件
     494                 if ( cur.disabled !== true || event.type !== "click" ) {
     495                     // 匹配的selector的缓存,避免每次调用is()方法进行判断
     496                     selMatch = {};
     497                     //暂存匹配selector的事件处理器
     498                     matches = [];
     499                     // 将其转换为jQuery对象,后面便可以调用is()方法
     500                     jqcur[0] = cur;
     501                     // 遍历代理handlers,查找匹配它们的selector
     502                     for ( i = 0; i < delegateCount; i++ ) {
     503                         handleObj = handlers[ i ];
     504                         sel = handleObj.selector;
     505 
     506                         if ( selMatch[ sel ] === undefined ) {
     507                             // 调用is方法,看是否匹配当前元素
     508                             selMatch[ sel ] = jqcur.is( sel );
     509                         }
     510                         // 如果匹配,那么放入matches里暂存
     511                         if ( selMatch[ sel ] ) {
     512                             matches.push( handleObj );
     513                         }
     514                     }
     515                     if ( matches.length ) {
     516                         // 将dom元素和对应的代理事件处理器以对象的形式统一添加到handlerQueue(与后面的普通事件处理器是一样的)
     517                         handlerQueue.push({ elem: cur, matches: matches });
     518                     }
     519                 }
     520             }
     521         }
     522 
     523         // Add the remaining (directly-bound) handlers
     524         // 将dom元素和对应的普通的事件处理器(非代理)添加到处理队列(handlerQueue)中去
     525         if ( handlers.length > delegateCount ) {
     526             handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
     527         }
     528 
     529         // Run delegates first; they may want to stop propagation beneath us
     530         // 优先执行代理处理器,因为有可能被代理的元素作为子元素会阻止传播
     531         for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
     532             matched = handlerQueue[ i ];
     533             // 正在触发事件回调的元素
     534             event.currentTarget = matched.elem;
     535             // 依次执行改元素对应的事件回调(在没有立刻阻止传播的情况下)
     536             for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
     537                 handleObj = matched.matches[ j ];
     538 
     539                 // Triggered event must either 1) be non-exclusive and have no namespace, or
     540                 // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
     541                 if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
     542                     // 注册事件处理器时传进来的数据
     543                     event.data = handleObj.data;
     544                     // 事件对应的事件处理器对象
     545                     event.handleObj = handleObj;
     546                     // 调用事件回调,特殊事件特殊处理
     547                     ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
     548                             .apply( matched.elem, args );
     549                     //如果返回值不是undefined,那么赋值到event.result中
     550                     if ( ret !== undefined ) {
     551                         event.result = ret;
     552                         //如果返回值是false,那么阻止冒泡传播和元素的默认操作
     553                         if ( ret === false ) {
     554                             event.preventDefault();
     555                             event.stopPropagation();
     556                         }
     557                     }
     558                 }
     559             }
     560         }
     561 
     562         // Call the postDispatch hook for the mapped type
     563         if ( special.postDispatch ) {
     564             special.postDispatch.call( this, event );
     565         }
     566         // 返回最后一次事件回调的值
     567         return event.result;
     568     },
     569 
     570     // Includes some event props shared by KeyEvent and MouseEvent
     571     // *** attrChange attrName relatedNode srcElement  are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
     572     props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
     573 
     574     // 所谓hook就是拦截修改的意思
     575     fixHooks: {},
     576 
     577     keyHooks: {
     578         props: "char charCode key keyCode".split(" "),
     579         filter: function( event, original ) {
     580 
     581             // Add which for key events
     582             if ( event.which == null ) {
     583                 event.which = original.charCode != null ? original.charCode : original.keyCode;
     584             }
     585 
     586             return event;
     587         }
     588     },
     589 
     590     mouseHooks: {
     591         props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
     592         filter: function( event, original ) {
     593             var eventDoc, doc, body,
     594                 button = original.button,
     595                 fromElement = original.fromElement;
     596 
     597             // Calculate pageX/Y if missing and clientX/Y available
     598             if ( event.pageX == null && original.clientX != null ) {
     599                 eventDoc = event.target.ownerDocument || document;
     600                 doc = eventDoc.documentElement;
     601                 body = eventDoc.body;
     602 
     603                 event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
     604                 event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
     605             }
     606 
     607             // Add relatedTarget, if necessary
     608             if ( !event.relatedTarget && fromElement ) {
     609                 event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
     610             }
     611 
     612             // Add which for click: 1 === left; 2 === middle; 3 === right
     613             // Note: button is not normalized, so don't use it
     614             if ( !event.which && button !== undefined ) {
     615                 event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
     616             }
     617 
     618             return event;
     619         }
     620     },
     621 
     622     // 修正event,利用统一的jQuery.Event兼容各个浏览器
     623     fix: function( event ) {
     624         // 如果已经fixed过,表明是jQuery.Event,则直接返回
     625         if ( event[ jQuery.expando ] ) {
     626             return event;
     627         }
     628 
     629         // Create a writable copy of the event object and normalize some properties
     630         var i, prop,
     631             originalEvent = event,
     632             fixHook = jQuery.event.fixHooks[ event.type ] || {},
     633             // 如果是key或者mouse事件,添加额外的属性(props)
     634             copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
     635 
     636         event = jQuery.Event( originalEvent );
     637 
     638         // 将浏览器原生event的属性赋值到新创建的jQuery.Event对象中去
     639         for ( i = copy.length; i; ) {
     640             prop = copy[ --i ];
     641             event[ prop ] = originalEvent[ prop ];
     642         }
     643 
     644         // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
     645         // 兼容触发事件的文档元素
     646         // Safari2下event.target可能为undefined
     647         if ( !event.target ) {
     648             event.target = originalEvent.srcElement || document;
     649         }
     650 
     651         // Target should not be a text node (#504, Safari)
     652         // 如果是文本节点,则取其parent
     653         if ( event.target.nodeType === 3 ) {
     654             event.target = event.target.parentNode;
     655         }
     656 
     657         // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8)
     658         event.metaKey = !!event.metaKey;
     659 
     660         return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
     661     },
     662 
     663     special: {
     664         load: {
     665             // Prevent triggered image.load events from bubbling to window.load
     666             noBubble: true
     667         },
     668 
     669         focus: {
     670             delegateType: "focusin"
     671         },
     672         blur: {
     673             delegateType: "focusout"
     674         },
     675 
     676         beforeunload: {
     677             setup: function( data, namespaces, eventHandle ) {
     678                 // We only want to do this special case on windows
     679                 if ( jQuery.isWindow( this ) ) {
     680                     this.onbeforeunload = eventHandle;
     681                 }
     682             },
     683 
     684             teardown: function( namespaces, eventHandle ) {
     685                 if ( this.onbeforeunload === eventHandle ) {
     686                     this.onbeforeunload = null;
     687                 }
     688             }
     689         }
     690     },
     691 
     692     simulate: function( type, elem, event, bubble ) {
     693         // Piggyback on a donor event to simulate a different one.
     694         // Fake originalEvent to avoid donor's stopPropagation, but if the
     695         // simulated event prevents default then we do the same on the donor.
     696         var e = jQuery.extend(
     697             new jQuery.Event(),
     698             event,
     699             { type: type,
     700                 isSimulated: true,
     701                 originalEvent: {}
     702             }
     703         );
     704         if ( bubble ) {
     705             jQuery.event.trigger( e, null, elem );
     706         } else {
     707             jQuery.event.dispatch.call( elem, e );
     708         }
     709         if ( e.isDefaultPrevented() ) {
     710             event.preventDefault();
     711         }
     712     }
     713 };
     714 
     715 // Some plugins are using, but it's undocumented/deprecated and will be removed.
     716 // The 1.7 special event interface should provide all the hooks needed now.
     717 jQuery.event.handle = jQuery.event.dispatch;
     718 
     719 // 调用原生的浏览器方法注销事件处理器,做兼容行处理
     720 jQuery.removeEvent = document.removeEventListener ?
     721     function( elem, type, handle ) {
     722         if ( elem.removeEventListener ) {
     723             elem.removeEventListener( type, handle, false );
     724         }
     725     } :
     726     function( elem, type, handle ) {
     727         var name = "on" + type;
     728 
     729         if ( elem.detachEvent ) {
     730 
     731             // #8545, #7054, preventing memory leaks for custom events in IE6-8 –
     732             // detachEvent needed property on element, by name of that event, to properly expose it to GC
     733             if ( typeof elem[ name ] === "undefined" ) {
     734                 elem[ name ] = null;
     735             }
     736 
     737             elem.detachEvent( name, handle );
     738         }
     739     };
     740 
     741 // jQuery为统一event对象而封装的Event类
     742 jQuery.Event = function( src, props ) {
     743     // Allow instantiation without the 'new' keyword
     744     // 兼容jQuery.Event()实例化Event对象
     745     if ( !(this instanceof jQuery.Event) ) {
     746         return new jQuery.Event( src, props );
     747     }
     748 
     749     // jQuery.Event object 或者 浏览器的Event object
     750     if ( src && src.type ) {
     751         // 存储原先的Event对象
     752         this.originalEvent = src;
     753         this.type = src.type;
     754 
     755         // Events bubbling up the document may have been marked as prevented
     756         // by a handler lower down the tree; reflect the correct value.
     757         this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
     758             src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
     759 
     760     // event type
     761     // event字面量,如:'click'
     762     } else {
     763         this.type = src;
     764     }
     765 
     766     // Put explicitly provided properties onto the event object
     767     // 提供的props将被extend进创建的event中作为属性
     768     if ( props ) {
     769         jQuery.extend( this, props );
     770     }
     771 
     772     // Create a timestamp if incoming event doesn't have one
     773     this.timeStamp = src && src.timeStamp || jQuery.now();
     774 
     775     // Mark it as fixed
     776     // 标记已经fiexed了
     777     this[ jQuery.expando ] = true;
     778 };
     779 
     780 // 函数的形式,避免直接修改属性值
     781 function returnFalse() {
     782     return false;
     783 }
     784 function returnTrue() {
     785     return true;
     786 }
     787 
     788 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
     789 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
     790 // jQuery.Event对象的公用方法
     791 jQuery.Event.prototype = {
     792     // 阻止元素默认操作,做兼容性处理
     793     preventDefault: function() {
     794         this.isDefaultPrevented = returnTrue;
     795 
     796         var e = this.originalEvent;
     797         if ( !e ) {
     798             return;
     799         }
     800 
     801         // if preventDefault exists run it on the original event
     802         if ( e.preventDefault ) {
     803             e.preventDefault();
     804 
     805         // otherwise set the returnValue property of the original event to false (IE)
     806         } else {
     807             e.returnValue = false;
     808         }
     809     },
     810     // 阻止元素冒泡传播,做兼容性处理
     811     stopPropagation: function() {
     812         this.isPropagationStopped = returnTrue;
     813 
     814         var e = this.originalEvent;
     815         if ( !e ) {
     816             return;
     817         }
     818         // if stopPropagation exists run it on the original event
     819         if ( e.stopPropagation ) {
     820             e.stopPropagation();
     821         }
     822         // otherwise set the cancelBubble property of the original event to true (IE)
     823         e.cancelBubble = true;
     824     },
     825     // 立刻阻止传播,指的是立即停止与本元素相关的之后的所有事件处理器以及其他元素的事件传播
     826     stopImmediatePropagation: function() {
     827         this.isImmediatePropagationStopped = returnTrue;
     828         this.stopPropagation();
     829     },
     830     isDefaultPrevented: returnFalse,
     831     isPropagationStopped: returnFalse,
     832     isImmediatePropagationStopped: returnFalse
     833 };
     834 
     835 // Create mouseenter/leave events using mouseover/out and event-time checks
     836 jQuery.each({
     837     mouseenter: "mouseover",
     838     mouseleave: "mouseout"
     839 }, function( orig, fix ) {
     840     jQuery.event.special[ orig ] = {
     841         delegateType: fix,
     842         bindType: fix,
     843 
     844         handle: function( event ) {
     845             var ret,
     846                 target = this,
     847                 related = event.relatedTarget,
     848                 handleObj = event.handleObj,
     849                 selector = handleObj.selector;
     850 
     851             // For mousenter/leave call the handler if related is outside the target.
     852             // NB: No relatedTarget if the mouse left/entered the browser window
     853             if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
     854                 event.type = handleObj.origType;
     855                 ret = handleObj.handler.apply( this, arguments );
     856                 event.type = fix;
     857             }
     858             return ret;
     859         }
     860     };
     861 });
     862 
     863 // IE submit delegation
     864 if ( !jQuery.support.submitBubbles ) {
     865 
     866     jQuery.event.special.submit = {
     867         setup: function() {
     868             // Only need this for delegated form submit events
     869             if ( jQuery.nodeName( this, "form" ) ) {
     870                 return false;
     871             }
     872 
     873             // Lazy-add a submit handler when a descendant form may potentially be submitted
     874             jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
     875                 // Node name check avoids a VML-related crash in IE (#9807)
     876                 var elem = e.target,
     877                     form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
     878                 if ( form && !jQuery._data( form, "_submit_attached" ) ) {
     879                     jQuery.event.add( form, "submit._submit", function( event ) {
     880                         event._submit_bubble = true;
     881                     });
     882                     jQuery._data( form, "_submit_attached", true );
     883                 }
     884             });
     885             // return undefined since we don't need an event listener
     886         },
     887 
     888         postDispatch: function( event ) {
     889             // If form was submitted by the user, bubble the event up the tree
     890             if ( event._submit_bubble ) {
     891                 delete event._submit_bubble;
     892                 if ( this.parentNode && !event.isTrigger ) {
     893                     jQuery.event.simulate( "submit", this.parentNode, event, true );
     894                 }
     895             }
     896         },
     897 
     898         teardown: function() {
     899             // Only need this for delegated form submit events
     900             if ( jQuery.nodeName( this, "form" ) ) {
     901                 return false;
     902             }
     903 
     904             // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
     905             jQuery.event.remove( this, "._submit" );
     906         }
     907     };
     908 }
     909 
     910 // IE change delegation and checkbox/radio fix
     911 if ( !jQuery.support.changeBubbles ) {
     912 
     913     jQuery.event.special.change = {
     914 
     915         setup: function() {
     916 
     917             if ( rformElems.test( this.nodeName ) ) {
     918                 // IE doesn't fire change on a check/radio until blur; trigger it on click
     919                 // after a propertychange. Eat the blur-change in special.change.handle.
     920                 // This still fires onchange a second time for check/radio after blur.
     921                 if ( this.type === "checkbox" || this.type === "radio" ) {
     922                     jQuery.event.add( this, "propertychange._change", function( event ) {
     923                         if ( event.originalEvent.propertyName === "checked" ) {
     924                             this._just_changed = true;
     925                         }
     926                     });
     927                     jQuery.event.add( this, "click._change", function( event ) {
     928                         if ( this._just_changed && !event.isTrigger ) {
     929                             this._just_changed = false;
     930                         }
     931                         // Allow triggered, simulated change events (#11500)
     932                         jQuery.event.simulate( "change", this, event, true );
     933                     });
     934                 }
     935                 return false;
     936             }
     937             // Delegated event; lazy-add a change handler on descendant inputs
     938             jQuery.event.add( this, "beforeactivate._change", function( e ) {
     939                 var elem = e.target;
     940 
     941                 if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) {
     942                     jQuery.event.add( elem, "change._change", function( event ) {
     943                         if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
     944                             jQuery.event.simulate( "change", this.parentNode, event, true );
     945                         }
     946                     });
     947                     jQuery._data( elem, "_change_attached", true );
     948                 }
     949             });
     950         },
     951 
     952         handle: function( event ) {
     953             var elem = event.target;
     954 
     955             // Swallow native change events from checkbox/radio, we already triggered them above
     956             if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
     957                 return event.handleObj.handler.apply( this, arguments );
     958             }
     959         },
     960 
     961         teardown: function() {
     962             jQuery.event.remove( this, "._change" );
     963 
     964             return !rformElems.test( this.nodeName );
     965         }
     966     };
     967 }
     968 
     969 // Create "bubbling" focus and blur events
     970 if ( !jQuery.support.focusinBubbles ) {
     971     jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
     972 
     973         // Attach a single capturing handler while someone wants focusin/focusout
     974         var attaches = 0,
     975             handler = function( event ) {
     976                 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
     977             };
     978 
     979         jQuery.event.special[ fix ] = {
     980             setup: function() {
     981                 if ( attaches++ === 0 ) {
     982                     document.addEventListener( orig, handler, true );
     983                 }
     984             },
     985             teardown: function() {
     986                 if ( --attaches === 0 ) {
     987                     document.removeEventListener( orig, handler, true );
     988                 }
     989             }
     990         };
     991     });
     992 }
     993 
     994 jQuery.fn.extend({
     995     // 注册事件处理器
     996     on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
     997         var origFn, type;
     998 
     999         // Types can be a map of types/handlers
    1000         // types可以为对象,是类型/函数对集合,如:
    1001         // {
    1002         //     'click' : fn1
    1003         //     ,'mouseover' : fn2
    1004         //     , ...
    1005         // }
    1006         if ( typeof types === "object" ) {
    1007             // ( types-Object, selector, data )
    1008             if ( typeof selector !== "string" ) { // && selector != null
    1009                 // ( types-Object, data )
    1010                 data = data || selector;
    1011                 selector = undefined;
    1012             }
    1013             for ( type in types ) {
    1014                 this.on( type, selector, data, types[ type ], one );
    1015             }
    1016             return this;
    1017         }
    1018 
    1019         // 以下是根据某些参数有无,对data和selector采取一定的判定匹配措施
    1020         if ( data == null && fn == null ) {
    1021             // ( types, fn )
    1022             fn = selector;
    1023             data = selector = undefined;
    1024         } else if ( fn == null ) {
    1025             if ( typeof selector === "string" ) {
    1026                 // ( types, selector, fn )
    1027                 fn = data;
    1028                 data = undefined;
    1029             } else {
    1030                 // ( types, data, fn )
    1031                 fn = data;
    1032                 data = selector;
    1033                 selector = undefined;
    1034             }
    1035         }
    1036         // 当fn赋值为false时,用返回值为false的函数替换,相当于是一个快捷键
    1037         if ( fn === false ) {
    1038             fn = returnFalse;
    1039         // 如果fn为空,则退出,什么都不做
    1040         } else if ( !fn ) {
    1041             return this;
    1042         }
    1043         // one参数是内部调用的,one为1表示事件只在内部被调用一次
    1044         if ( one === 1 ) {
    1045             origFn = fn;
    1046             fn = function( event ) {
    1047                 // Can use an empty set, since event contains the info
    1048                 // 创建jQuery空对象,调用off方法,传入带有信息的event参数(其中包括注册时的所有信息),在off中会对
    1049                 // event对象做特殊处理,从而删除指定type的handler,保证只调用一次
    1050                 jQuery().off( event );
    1051                 return origFn.apply( this, arguments );
    1052             };
    1053             // Use same guid so caller can remove using origFn
    1054             fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
    1055         }
    1056         return this.each( function() {
    1057             jQuery.event.add( this, types, fn, data, selector );
    1058         });
    1059     },
    1060     // 注册只能被触发一次的事件处理器
    1061     one: function( types, selector, data, fn ) {
    1062         return this.on( types, selector, data, fn, 1 );
    1063     },
    1064     off: function( types, selector, fn ) {
    1065         var handleObj, type;
    1066         // 如果传进来的是event对象,那么进行如下处理(因为event对象包含off事件处理器需要的全部信息)
    1067         if ( types && types.preventDefault && types.handleObj ) {
    1068             // ( event )  dispatched jQuery.Event
    1069             handleObj = types.handleObj;
    1070             jQuery( types.delegateTarget ).off(
    1071                 handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
    1072                 handleObj.selector,
    1073                 handleObj.handler
    1074             );
    1075             return this;
    1076         }
    1077         //types可以为对象,是类型/函数对集合,同上面的on
    1078         if ( typeof types === "object" ) {
    1079             // ( types-object [, selector] )
    1080             for ( type in types ) {
    1081                 this.off( type, selector, types[ type ] );
    1082             }
    1083             return this;
    1084         }
    1085         // 只有两个参数的情况
    1086         if ( selector === false || typeof selector === "function" ) {
    1087             // ( types [, fn] )
    1088             fn = selector;
    1089             selector = undefined;
    1090         }
    1091         // 当fn赋值为false时,用返回值为false的函数替换,相当于是一个快捷键
    1092         if ( fn === false ) {
    1093             fn = returnFalse;
    1094         }
    1095         return this.each(function() {
    1096             jQuery.event.remove( this, types, fn, selector );
    1097         });
    1098     },
    1099     // 绑定事件,与on的区别在于不提供selector,这意味着它不支持事件代理
    1100     bind: function( types, data, fn ) {
    1101         return this.on( types, null, data, fn );
    1102     },
    1103     unbind: function( types, fn ) {
    1104         return this.off( types, null, fn );
    1105     },
    1106     // 与bind类似,但是它支持后绑定
    1107     live: function( types, data, fn ) {
    1108         // 默认不指定上下问的情况下,代理元素是document,所以说live其实是用的事件代理实现的
    1109         jQuery( this.context ).on( types, this.selector, data, fn );
    1110         return this;
    1111     },
    1112     die: function( types, fn ) {
    1113         jQuery( this.context ).off( types, this.selector || "**", fn );
    1114         return this;
    1115     },
    1116     // 事件代理接口,根本上就是调用on
    1117     delegate: function( selector, types, data, fn ) {
    1118         return this.on( types, selector, data, fn );
    1119     },
    1120     undelegate: function( selector, types, fn ) {
    1121         // ( namespace ) or ( selector, types [, fn] )
    1122         return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
    1123     },
    1124     // 触发指定类型的事件回调函数列表
    1125     trigger: function( type, data ) {
    1126         return this.each(function() {
    1127             jQuery.event.trigger( type, data, this );
    1128         });
    1129     },
    1130     // 与trigger几乎相同,不同的是,它只对jQuery集合中的第一个元素trigger,而且不冒泡,不执行默认操作
    1131     // 其内部调用trigger,用最后一个参数来区别
    1132     triggerHandler: function( type, data ) {
    1133         if ( this[0] ) {
    1134             return jQuery.event.trigger( type, data, this[0], true );
    1135         }
    1136     },
    1137 
    1138     toggle: function( fn ) {
    1139         // Save reference to arguments for access in closure
    1140         var args = arguments,
    1141             guid = fn.guid || jQuery.guid++,
    1142             i = 0,
    1143             toggler = function( event ) {
    1144                 // Figure out which function to execute
    1145                 var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
    1146                 jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
    1147 
    1148                 // Make sure that clicks stop
    1149                 event.preventDefault();
    1150 
    1151                 // and execute the function
    1152                 return args[ lastToggle ].apply( this, arguments ) || false;
    1153             };
    1154 
    1155         // link all the functions, so any of them can unbind this click handler
    1156         toggler.guid = guid;
    1157         while ( i < args.length ) {
    1158             args[ i++ ].guid = guid;
    1159         }
    1160 
    1161         return this.click( toggler );
    1162     },
    1163     // 模拟hover事件
    1164     hover: function( fnOver, fnOut ) {
    1165         return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
    1166     }
    1167 });
    1168 // 绑定和注册事件的快捷键,内部还是调用on()或者trigger()
    1169 // 初始化jQuery.event.fixHooks
    1170 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
    1171     "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
    1172     "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
    1173 
    1174     // Handle event binding
    1175     jQuery.fn[ name ] = function( data, fn ) {
    1176         if ( fn == null ) {
    1177             fn = data;
    1178             data = null;
    1179         }
    1180 
    1181         return arguments.length > 0 ?
    1182             // 绑定事件
    1183             this.on( name, null, data, fn ) :
    1184             // 触发事件,不提供数据data
    1185             this.trigger( name );
    1186     };
    1187 
    1188     
    1189     // keyHooks
    1190     if ( rkeyEvent.test( name ) ) {
    1191         jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
    1192     }
    1193     // mouseHooks
    1194     if ( rmouseEvent.test( name ) ) {
    1195         jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
    1196     }
    1197 });


  • 相关阅读:
    统计八连块
    linux-shell编程-添加用户并设置权限
    chrome 的网站测试工具
    windows10安装自带的ubuntu子系统
    开源项目阅读笔记--appium+adb
    TODO 软件质量模型--理论
    java -static的特性和使用,静态类/方法/块/内部类/回收机制
    TODO 竞品分析方法——关于导航评测的一些笔记
    mock工具:mock.js 和vscode faker,moco
    移动App性能评测与优化-Android内存测试 ,DVM原理
  • 原文地址:https://www.cnblogs.com/lovesueee/p/2778745.html
Copyright © 2011-2022 走看看