第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > 【原创】jQuery1.8.2源码解析之jQuery.event

【原创】jQuery1.8.2源码解析之jQuery.event

时间:2019-05-28 21:42:00

相关推荐

【原创】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, 2rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/, 3rhoverHack = /(?:^|\s)hover(\.\S+|)\b/, 4rkeyEvent = /^key/, 5rmouseEvent = /^(?:mouse|contextmenu)|click/, 6rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, 7hoverHack = 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 17add: 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 ) { 129elem.addEventListener( type, eventHandle, false ); 130 131 } else if ( elem.attachEvent ) { 132elem.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 164global: {}, 165 166// Detach an event or set of events from an element 167remove: 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事件,命名空间是'',处理器为fn1,那么'click.aa',''都是可以用来指定并删除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// 事件代理处理器个数减减 226eventType.delegateCount--; 227 } 228 if ( special.remove ) { 229special.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. 260customEvent: { 261 "getData": true, 262 "setData": true, 263 "changeData": true 264}, 265 266// onlyHandlers在调用triggerHandler时使用 267trigger: 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// 暂时去除事件处理器 424elem[ 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 ) { 435elem[ ontype ] = old; 436 } 437 } 438 } 439 } 440 441 return event.result; 442}, 443 444dispatch: 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++ ) { 503handleObj = handlers[ i ]; 504sel = handleObj.selector; 505 506if ( selMatch[ sel ] === undefined ) { 507 // 调用is方法,看是否匹配当前元素 508 selMatch[ sel ] = jqcur.is( sel ); 509} 510// 如果匹配,那么放入matches里暂存 511if ( selMatch[ sel ] ) { 512 matches.push( handleObj ); 513} 514 } 515 if ( matches.length ) { 516// 将dom元素和对应的代理事件处理器以对象的形式统一添加到handlerQueue(与后面的普通事件处理器是一样的) 517handlerQueue.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 ) { 551event.result = ret; 552//如果返回值是false,那么阻止冒泡传播和元素的默认操作 553if ( 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 *** 572props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), 573 574// 所谓hook就是拦截修改的意思 575fixHooks: {}, 576 577keyHooks: { 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 590mouseHooks: { 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兼容各个浏览器 623fix: 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 663special: { 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 692simulate: 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 ? 721function( elem, type, handle ) { 722 if ( elem.removeEventListener ) { 723 elem.removeEventListener( type, handle, false ); 724 } 725} : 726function( 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对象 745if ( !(this instanceof jQuery.Event) ) { 746 return new jQuery.Event( src, props ); 747} 748 749// jQuery.Event object 或者 浏览器的Event object 750if ( 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中作为属性 768if ( props ) { 769 jQuery.extend( this, props ); 770} 771 772// Create a timestamp if incoming event doesn't have one 773this.timeStamp = src && src.timeStamp || jQuery.now(); 774 775// Mark it as fixed 776// 标记已经fiexed了 777this[ jQuery.expando ] = true; 778 }; 779 780 // 函数的形式,避免直接修改属性值 781 function returnFalse() { 782return false; 783 } 784 function returnTrue() { 785return true; 786 } 787 788 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding 789 // /TR//WD-DOM-Level-3-Events-0331/ecma-script-binding.html 790 // jQuery.Event对象的公用方法 791 jQuery.Event.prototype = { 792// 阻止元素默认操作,做兼容性处理 793preventDefault: 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// 阻止元素冒泡传播,做兼容性处理 811stopPropagation: 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// 立刻阻止传播,指的是立即停止与本元素相关的之后的所有事件处理器以及其他元素的事件传播 826stopImmediatePropagation: function() { 827 this.isImmediatePropagationStopped = returnTrue; 828 this.stopPropagation(); 829}, 830isDefaultPrevented: returnFalse, 831isPropagationStopped: returnFalse, 832isImmediatePropagationStopped: returnFalse 833 }; 834 835 // Create mouseenter/leave events using mouseover/out and event-time checks 836 jQuery.each({ 837mouseenter: "mouseover", 838mouseleave: "mouseout" 839 }, function( orig, fix ) { 840jQuery.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 866jQuery.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 ) { 880event._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 913jQuery.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 ) { 923if ( event.originalEvent.propertyName === "checked" ) { 924 this._just_changed = true; 925} 926 }); 927 jQuery.event.add( this, "click._change", function( event ) { 928if ( this._just_changed && !event.isTrigger ) { 929 this._just_changed = false; 930} 931// Allow triggered, simulated change events (#11500) 932jQuery.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 ) { 943if ( 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 ) { 971jQuery.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// 注册事件处理器 996on: function( types, selector, data, fn, /*INTERNAL*/ one ) { 997 var origFn, type; 998 999 // Types can be a map of types/handlers1000 // types可以为对象,是类型/函数对集合,如:1001 // {1002 //'click' : fn11003 //,'mouseover' : fn21004 //, ...1005 // }1006 if ( typeof types === "object" ) {1007 // ( types-Object, selector, data )1008 if ( typeof selector !== "string" ) { // && selector != null1009 // ( 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 info1048 // 创建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 origFn1054 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// 注册只能被触发一次的事件处理器1061one: function( types, selector, data, fn ) {1062 return this.on( types, selector, data, fn, 1 );1063},1064off: function( types, selector, fn ) {1065 var handleObj, type;1066 // 如果传进来的是event对象,那么进行如下处理(因为event对象包含off事件处理器需要的全部信息)1067 if ( types && types.preventDefault && types.handleObj ) {1068 // ( event ) dispatched jQuery.Event1069 handleObj = types.handleObj;1070 jQuery( types.delegateTarget ).off(1071 handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,1072 handleObj.selector,1073 handleObj.handler1074 );1075 return this;1076 }1077 //types可以为对象,是类型/函数对集合,同上面的on1078 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,这意味着它不支持事件代理1100bind: function( types, data, fn ) {1101 return this.on( types, null, data, fn );1102},1103unbind: function( types, fn ) {1104 return this.off( types, null, fn );1105},1106// 与bind类似,但是它支持后绑定1107live: function( types, data, fn ) {1108 // 默认不指定上下问的情况下,代理元素是document,所以说live其实是用的事件代理实现的1109 jQuery( this.context ).on( types, this.selector, data, fn );1110 return this;1111},1112die: function( types, fn ) {1113 jQuery( this.context ).off( types, this.selector || "**", fn );1114 return this;1115},1116// 事件代理接口,根本上就是调用on1117delegate: function( selector, types, data, fn ) {1118 return this.on( types, selector, data, fn );1119},1120undelegate: 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// 触发指定类型的事件回调函数列表1125trigger: function( type, data ) {1126 return this.each(function() {1127 jQuery.event.trigger( type, data, this );1128 });1129},1130// 与trigger几乎相同,不同的是,它只对jQuery集合中的第一个元素trigger,而且不冒泡,不执行默认操作1131// 其内部调用trigger,用最后一个参数来区别1132triggerHandler: function( type, data ) {1133 if ( this[0] ) {1134 return jQuery.event.trigger( type, data, this[0], true );1135 }1136},1137 1138toggle: function( fn ) {1139 // Save reference to arguments for access in closure1140 var args = arguments,1141 guid = fn.guid || jQuery.guid++,1142 i = 0,1143 toggler = function( event ) {1144 // Figure out which function to execute1145 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 stop1149 event.preventDefault();1150 1151 // and execute the function1152 return args[ lastToggle ].apply( this, arguments ) || false;1153 };1154 1155 // link all the functions, so any of them can unbind this click handler1156 toggler.guid = guid;1157 while ( i < args.length ) {1158 args[ i++ ].guid = guid;1159 }1160 1161 return this.click( toggler );1162},1163// 模拟hover事件1164hover: function( fnOver, fnOut ) {1165 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );1166}1167 });1168 // 绑定和注册事件的快捷键,内部还是调用on()或者trigger()1169 // 初始化jQuery.event.fixHooks1170 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 binding1175jQuery.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 // 触发事件,不提供数据data1185 this.trigger( name );1186};1187 11881189// keyHooks1190if ( rkeyEvent.test( name ) ) {1191 jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;1192}1193// mouseHooks1194if ( rmouseEvent.test( name ) ) {1195 jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;1196}1197 });

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。