日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

jQuery1.9.1源码分析--Events模块

發布時間:2025/3/15 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jQuery1.9.1源码分析--Events模块 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1 var rformElems = /^(?:input|select|textarea)$/i, 2 rkeyEvent = /^key/, 3 rmouseEvent = /^(?:mouse|contextmenu)|click/, 4 rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, 5 rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; 6 7 function returnTrue() { 8 return true; 9 } 10 11 function returnFalse() { 12 return false; 13 } 14 15 jQuery.event = { 16 global: {}, 17 /** 18 * 事件綁定最后都通過jQuery.event.add來實現。其執行過程大致如下: 19 1. 先調用jQuery._data從$.cache中取出已有的事件緩存(私有數據,Cache的解析詳見數據緩存) 20 2. 如果是第一次在DOM元素上綁定該類型事件句柄,在DOM元素上綁定jQuery.event.handle,作為統一的事件響應入口 21 3. 將封裝后的事件句柄放入緩存中 22 傳入的事件句柄,會被封裝到對象handleObj的handle屬性上,此外handleObj還會填充guid、type、namespace、data屬性;DOM事件句柄elemData.handle指向jQuery.event.handle,即jQuery在DOM元素上綁定事件時總是綁定同樣的DOM事件句柄jQuery.event.handle。 23 事件句柄在緩存$.cache中的數據結構如下,事件類型和事件句柄都存儲在屬性events中,屬性handle存放的執行這些事件句柄的DOM事件句柄: 24 elemData = { 25 events: { 26 'click' : [ 27 { guid: 5, type: 'click', namespace: '', data: undefined, 28 handle: { guid: 5, prototype: {} } 29 }, 30 { ... } 31 ], 32 'keypress' : [ ... ] 33 }, 34 handle: { // DOM事件句柄 35 elem: elem, 36 prototype: {} 37 } 38 } 39 */ 40 add: function(elem, types, handler, data, selector) { 41 var tmp, events, t, handleObjIn, 42 special, eventHandle, handleObj, 43 handlers, type, namespaces, origType, 44 // 創建或獲取私有的緩存數據 45 elemData = jQuery._data(elem); 46 47 if (!elemData) { 48 return; 49 } 50 51 // 可以給jq的handler對象傳參數配置 52 if (handler.handler) { 53 handleObjIn = handler; 54 handler = handleObjIn.handler; 55 selector = handleObjIn.selector; 56 } 57 58 // 確保處理程序有唯一ID,以便查找和刪除 59 // handler函數添加guid屬性 60 if (!handler.guid) { 61 handler.guid = jQuery.guid++; 62 } 63 64 // 首次初始化元素的事件結構和主要處理程序 65 // 緩存數據elemData添加events屬性對象 66 if (!(events = elemData.events)) { 67 events = elemData.events = {}; 68 } 69 // elemData添加handle方法 70 if (!(eventHandle = elemData.handle)) { 71 // 當我們使用jQuery為元素添加事件處理程序時, 72 // 實際上就是調用了這個通過包裝的函數, 73 // 而這里面就是通過jQuery.event.dispatch方法來觸發的 74 eventHandle = elemData.handle = function(e) { 75 // 如果jQuery完成初始化且不存在e或者已經jQuery.event.trigger()了 76 // 返回派遣委托后的結果 77 // this指向eventHandle.elem,解決ie中注冊事件this指向的問題 78 // 如果是IE,這里使用attachEvent監聽,其事件處理程序的第一個參數就有ie的event了。 79 // 平時說的window.event是指在elem['on' + type] = handler;的情況 80 return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? jQuery.event.dispatch.apply(eventHandle.elem, arguments) : 81 undefined; 82 }; 83 // 給handle函數添加elem屬性防止IE非原生內存泄露 84 // handle方法添加elem屬性 85 eventHandle.elem = elem; 86 } 87 88 // 處理空格分離的多事件 89 // jQuery(...).bind("mouseover mouseout", fn); 90 types = (types || '').match(core_rnotwhite) || ['']; 91 t = types.length; 92 while (t--) { 93 tmp = rtypenamespace.exec(types[t]) || []; 94 type = origType = tmp[1]; 95 // 對命名空間進行排序 96 // click.a.c.f.d --- a.c.d.f 97 namespaces = (tmp[2] || '').split('.').sort(); 98 99 // 事件特例(就是為一些事件類型的一些特殊情況的處理) 100 special = jQuery.event.special[type] || {}; 101 102 // 如果有事件特例,就使用。否則還是使用原始type 103 type = (selector ? special.delegateType : special.bindType) || type; 104 105 // 更新事件特例的類型 106 special = jQuery.event.special[type] || {}; 107 108 // 給handleObj添加事件處理程序相關信息, 109 // 如果target對象有相同屬性或方法則替換為handleObj的 110 handleObj = jQuery.extend({ 111 type: type, 112 origType: origType, 113 data: data, 114 handler: handler, 115 guid: handler.guid, 116 selector: selector, 117 needsContext: selector && jQuery.expr.match.needsContext.test(selector), 118 namespace: namespaces.join('.') 119 }, handleObjIn); 120 121 // 首次初始化事件處理程序隊列 122 if (!(handlers = events[type])) { 123 handlers = events[type] = []; 124 handlers.delegateCount = 0; 125 126 // 當事件特例處理程序沒有setup方法或者setup返回false時使用addEventListener/attachEvent 127 if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) { 128 // 給元素綁定事件處理程序,知道這里才真正添加事件處理程序 129 if (elem.addEventListener) { 130 elem.addEventListener(type, eventHandle, false); 131 } else if (elem.attachEvent) { 132 elem.attachEvent('on' + type, eventHandle); 133 } 134 } 135 } 136 137 // 事件特例的一些處理 138 if (special.add) { 139 special.add.call(elem, handleObj); 140 141 if (!handleObj.handler.guid) { 142 handleObj.handler.guid = handler.guid; 143 } 144 } 145 146 // 添加元素的事件處理列表, 147 // 如果有selector,則用來給委托事件使用的 148 if (selector) { 149 handlers.splice(handlers.delegateCount++, 0, handleObj); 150 } else { 151 handlers.push(handleObj); 152 } 153 154 // 追蹤哪個事件曾經被運行過 155 jQuery.event.global[type] = true; 156 } 157 158 // 防止IE內存泄露 159 elem = null; 160 }, 161 /** 162 * 注銷元素的事件或者事件集 163 * 164 * 通過jQuery.event.remove實現,其執行過程大致如下: 165 1. 現調用jQuery._data從緩存$.cache中取出elem對應的所有數組(內部數據,與調用jQuery.data存儲的數據稍有不同 166 2. 如果未傳入types則移除所有事件句柄,如果types是命名空間,則移除所有與命名空間匹配的事件句柄 167 3. 如果是多個事件,則分割后遍歷 168 4. 如果未指定刪除哪個事件句柄,則刪除事件類型對應的全部句柄,或者與命名空間匹配的全部句柄 169 5. 如果指定了刪除某個事件句柄,則刪除指定的事件句柄 170 6. 所有的事件句柄刪除,都直接在事件句柄數組jQuery._data( elem ).events[ type ]上調用splice操作 171 7. 最后檢查事件句柄數組的長度,如果為0,或為1但要刪除,則移除綁定在elem上DOM事件 172 8. 最后的最后,如果elem對應的所有事件句柄events都已刪除,則從緩存中移走elem的內部數據 173 9. 在以上的各個過程,都要檢查是否有特例需要處理 174 */ 175 remove: function(elem, types, handler, selector, mappedTypes) { 176 var j, handleObj, tmp, 177 origCount, t, events, 178 special, handlers, type, 179 namespaces, origType, 180 elemData = jQuery.hasData(elem) && jQuery._data(elem); 181 182 if (!elemData || !(events = elemData.events)) { 183 return; 184 } 185 186 types = (types || '').match(core_rnotwhite) || ['']; 187 t = types.length; 188 while (t--) { 189 tmp = rtypenamespace.exec(types[t]) || []; 190 type = origType = tmp[1]; 191 namespaces = (tmp[2] || '').split('.').sort(); 192 193 // 如果沒有指定type,解綁元素的所有事件(包括命名空間上的) 194 if (!type) { 195 for (type in events) { 196 jQuery.event.remove(elem, type + types[t], handler, selector, true); 197 } 198 continue; 199 } 200 201 special = jQuery.event.special[type] || {}; 202 type = (selector ? special.delegateType : special.bindType) || type; 203 // 該事件列表 204 handlers = events[type] || []; 205 tmp = tmp[2] && new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)"); 206 207 // 刪除匹配的事件 208 209 // 事件列表的長度 210 origCount = j = handlers.length; 211 while (j--) { 212 handleObj = handlers[j]; 213 214 if ((mappedTypes || origType === handleObj.origType) && 215 (!handler || handler.guid === handleObj.guid) && 216 (!tmp || tmp.test(handleObj.namespace)) && 217 (!selector || selector === handleObj.selector || selector === "**" && handleObj.selector)) { 218 // 刪除events事件列表中的該項 219 handlers.splice(j, 1); 220 // 如果有委托,delegateCount就減一 221 if (handleObj.selector) { 222 handlers.delegateCount--; 223 } 224 if (special.remove) { 225 special.remove.call(elem, handleObj); 226 } 227 } 228 } 229 230 // 刪除通用的事件處理程序,同時避免無限遞歸 231 232 // 如果原始事件列表有項,經過前面的步驟長度為0 233 if (origCount && !handlers.length) { 234 if (!special.teardown || special.teardown.call(elem, namespaces, elemData.handle) === false) { 235 // 刪除注冊的偵聽事件 236 jQuery.removeEvent(elem, type, elemData.handle); 237 } 238 239 // 刪除events[type]屬性 240 delete events[type]; 241 } 242 } 243 244 // 如果events不再使用則刪除 245 if (jQuery.isEmptyObject(events)) { 246 delete elemData.handle; 247 248 // 使用removeData檢查空的和清空expando 249 jQuery._removeData(elem, 'events'); 250 } 251 }, 252 /** 253 * 254 * 255 * 1.可觸發自定義事件 256 * 2.觸發原生事件處理程序 257 * 1).通過jQuery定義的 258 * 2).如果觸發該類型事件都會觸發elem[type]和elem['on' + type]方法,如果沒有冒泡阻止,也會觸發其他冒泡路徑上的元素的ontype方法 259 * 260 * @param event 261 * @param data 262 * @param elem 263 * @param onlyHandlers 264 * @returns {*} 265 */ 266 trigger: function(event, data, elem, onlyHandlers) { 267 var handle, ontype, cur, 268 bubbleType, special, tmp, i, 269 eventPath = [elem || document], 270 type = core_hasOwn.call(event, 'type') ? event.type : event, 271 namespaces = core_hasOwn.call(event, 'namespace') ? event.namespace.split('.') : []; 272 273 cur = tmp = elem = elem || document; 274 275 if (elem.nodeType === 3 || elem.nodeType === 8) { 276 return; 277 } 278 279 // focus/blur變形為focusin/out,確保我們不會立刻觸發它們 280 if (rfocusMorph.test(type + jQuery.event.triggered)) { 281 return; 282 } 283 284 if (type.indexOf('.') >= 0) { 285 namespaces = type.split('.'); 286 // 取出第一項,事件類型 287 type = namespaces.shift(); 288 // 命名空間排序 289 namespaces.sort(); 290 } 291 ontype = type.indexOf(':') < 0 && 'on' + type; 292 293 // 確保是jQuery的event對象 294 event = event[jQuery.expando] ? 295 event : 296 new jQuery.Event(type, typeof event === 'object' && event); 297 298 event.isTrigger = true; 299 event.namespace = namespaces.join('.'); 300 event.namespace_re = event.namespace ? 301 new RegExp('(^|\\.)' + namespaces.join('\\.(?:.*\\.|)') + '(\\.|$)') : 302 null; 303 304 // 清除事件,防止被重用 305 event.result = undefined; 306 if (!event.target) { 307 event.target = elem; 308 } 309 310 // 克隆來源數據和預先準備事件,創建處理程序參數列表 311 data = data == null ? 312 [event] : 313 jQuery.makeArray(data, [event]); 314 315 // 特殊的情況下的trigger 316 special = jQuery.event.special[type] || {}; 317 if (!onlyHandlers && special.trigger && special.trigger.apply(elem, data) === false) { 318 return; 319 } 320 321 // 保存冒泡時經過的元素到eventPath中,向上冒到document,然后到window;也可能是全局ownerDocument變量 322 if (!onlyHandlers && !special.noBubble && !jQuery.isWindow(elem)) { 323 bubbleType = special.delegateType || type; 324 if (!rfocusMorph.test(bubbleType + type)) { 325 // 如果不是focus/blur類型,將當前元素改為父節點元素 326 cur = cur.parentNode; 327 } 328 // 一直向上獲取父輩元素并存入eventPath數組中 329 for (; cur; cur = cur.parentNode) { 330 eventPath.push(cur); 331 tmp = cur; 332 } 333 334 // 如tmp到了document,我們添加window對象 335 if (tmp === (elem.ownerDocument || document)) { 336 eventPath.push(tmp.defaultView || tmp.parentWindow || window); 337 } 338 } 339 340 // 在事件路徑上觸發處理程序, 如果沒有阻止冒泡就會遍歷eventPath, 341 // 如果當前元素對應的事件類型有事件處理程序,就執行它,直到到最頂元素。 342 // 如果阻止,在第一次遍歷后就不會再遍歷了。 343 i = 0; 344 while ((cur = eventPath[i++]) && !event.isPropagationStopped()) { 345 event.type = i > 1 ? 346 bubbleType : 347 special.bindType || type; 348 349 // jQuery 緩存中的處理程序 350 handle = (jQuery._data(cur, 'events') || {})[event.type] && jQuery._data(cur, 'handle'); 351 // 如果有handle方法,執行它。這里的handle是元素綁定的事件 352 if (handle) { 353 handle.apply(cur, data); 354 } 355 356 // 觸發原生處理程序elem['on' + type] 357 handle = ontype && cur[ontype]; 358 if (handle && jQuery.acceptData(cur) && handle.apply && handle.apply(cur, data) === false) { 359 event.preventDefault(); 360 } 361 } 362 event.type = type; 363 364 // 如果沒有阻止默認行為動作,處理elem的type屬性事件, 365 // 執行elem[type]處理程序但不會觸發elem['on' + type] 366 if (!onlyHandlers && !event.isDefaultPrevented()) { 367 // 1. 368 // 1).沒有special._default 369 // 2).有special._default,該方法的執行結果返回false 370 // 2. 371 // type不能使click且elem不能使a標簽 372 // 3. 373 // elem可接受緩存 374 if ((!special._default || special._default.apply(elem.ownerDocument, data) === false) && !(type === 'click' && jQuery.nodeName(elem, 'a')) && jQuery.acceptData(elem)) { 375 376 if (ontype && elem[type] && !jQuery.isWindow(elem)) { 377 // 緩存older 378 tmp = elem[ontype]; 379 380 // 當我們執行foo()時,不會重新觸發onfoo事件 381 if (tmp) { 382 elem[ontype] = null; 383 } 384 385 // 防止再次觸發中的相同事件,第一次觸發完后jQuery.event.triggered = undefined 386 jQuery.event.triggered = type; 387 try { 388 // 執行方法 389 elem[type](); 390 } catch (e) { 391 // 隱藏元素在focus/blur時,ie9以下會奔潰 392 } 393 jQuery.event.triggered = undefined; 394 395 if (tmp) { 396 elem[ontype] = tmp; 397 } 398 } 399 } 400 } 401 402 return event.result; 403 }, 404 /** 405 * 派遣事件 406 * 創建jQuery的event對象來代理訪問原生的event, 407 * 通過jQuery.event.handlers計算出委托事件處理隊列handlerQueue(冒泡路徑上的元素),沒有委托則保存著當前元素和保存著其事件處理相關信息的對象handleObj。 408 * 遍歷委托事件處理隊列,再遍歷事件處理數組,找到匹配的事件類型,如果有處理程序,就執行它。可以使用event.stopImmediatePropagation()來阻止遍歷下一個事件處理數組項。如果當前元素的當前事件處理程序返回值是false或者內部使用了event.stopPropagation()。就不會遍歷下一個冒泡路徑上的元素了(即當前元素的父級上的元素) 409 * jQuery.event.special[event.type].preDispatch和jQuery.event.special[event.type].postDispatch分別是派遣事件開始和結束的鉤子方法。 410 * @param event 原生event對象 411 * @returns {result|*} 412 */ 413 dispatch: function(event) { 414 // 從原生event中創建jq的event 415 event = jQuery.event.fix(event); 416 417 var i, ret, handleObj, matched, j, 418 handlerQueue = [], 419 args = core_slice.call(arguments), 420 // 獲取元素在jQuery.cache中的events對象的type數組 421 handlers = (jQuery._data(this, 'events') || {})[event.type] || [], 422 // 事件特例 423 special = jQuery.event.special[event.type] || {}; 424 425 // 將第一個event參數替換為jq的event 426 args[0] = event; 427 // 設置委托目標 428 event.delegateTarget = this; 429 430 // 如果存在preDispatch鉤子,則運行該方法后退出 431 if (special.preDispatch && special.preDispatch.call(this, event) === false) { 432 return; 433 } 434 435 // 委托事件隊列 436 handlerQueue = jQuery.event.handlers.call(this, event, handlers); 437 438 // 先運行委托,如果阻止了冒泡就停止循環 439 i = 0; 440 while ((matched = handlerQueue[i++]) && !event.isPropagationStopped()) { 441 event.currentTarget = matched.elem; 442 443 j = 0; 444 445 // 遍歷當前元素的事件處理程序數組 446 while ((handleObj = matched.handlers[j++]) && !event.isImmediatePropagationStopped()) { 447 // 被觸發的時間不能有命名空間或者有命名空間,且被綁定的事件是命名空間的一個子集 448 if (!event.namespace_re || event.namespace_re.test(handleObj.namespace)) { 449 event.handleObj = handleObj; 450 event.data = handleObj.data; 451 452 // 嘗試通過事件特例觸發handle方法,如果沒有則觸發handleObj的handler方法 453 // mouseenter/mouseleave事件特例就是使用了該handle方法, 454 // 事件特例的handle方法就是相當于一個裝飾者, 455 // 把handleObj.handler包裝了起來 456 ret = ((jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler).apply(matched.elem, args); 457 458 // 如果ret有值且是false則阻止默認行為和冒泡 459 // 即return false的時候阻止默認行為和冒泡 460 if (ret !== undefined) { 461 if ((event.result = ret) === false) { 462 event.preventDefault(); 463 event.stopPropagation(); 464 } 465 } 466 } 467 } 468 } 469 470 // 運行postDispatch鉤子方法 471 if (special.postDispatch) { 472 special.postDispatch.call(this, event); 473 } 474 475 return event.result; 476 }, 477 // 處理委托事件的方法,返回一個隊列,隊列中每個元素有當前元素和匹配到的handler 478 handlers: function(event, handlers) { 479 var sel, handleObj, matches, i, 480 handlerQueue = [], 481 delegateCount = handlers.delegateCount, 482 // 當前時間元素 483 cur = event.target; 484 485 // 是否有委托 486 if (delegateCount && cur.nodeType && (!event.button || event.type !== 'click')) { 487 // 遍歷父輩元素,直到找到委托元素this 488 for (; cur != this; cur = cur.parentNode || this) { 489 // 確保是元素且未禁用或者非點擊事件 490 if (cur.nodeType === 1 && (cur.disabled !== true || event.type !== 'click')) { 491 matches = []; 492 // 遍歷被委托事件處理程序,handlers[i]為jq的handler對象 493 for (i = 0; i < delegateCount; i++) { 494 handleObj = handlers[i]; 495 496 // 當前handler的選擇器字符, 加空格字符串是為了防止和Object.prototype屬性沖突 497 sel = handleObj.selector + ' '; 498 499 // matches[sel]保存著當前元素是否在受委托元素中的標記 500 if (matches[sel] === undefined) { 501 matches[sel] = handleObj.needsContext ? 502 jQuery(sel, this).index(cur) >= 0 : 503 jQuery.find(sel, this, null, [cur]).length; 504 } 505 // 如果當前元素是在受委托元素中,則將當前handlerObj推入到matches數組中 506 if (matches[sel]) { 507 matches.push(handleObj); 508 } 509 } 510 // 如果matches數組有內容,則將新對象推入handlerQueue隊列中 511 // elem保存著當前元素,handlers這保存著當前元素匹配的handlers 512 if (matches.length) { 513 handlerQueue.push({ 514 elem: cur, 515 handlers: matches 516 }); 517 } 518 } 519 } 520 } 521 522 // 如果handlers還有剩余,把剩余的部分也推入到隊列中 523 if (delegateCount < handlers.length) { 524 handlerQueue.push({ 525 elem: this, 526 handlers: handlers.slice(delegateCount) 527 }); 528 } 529 530 return handlerQueue; 531 }, 532 // 創建一個jq event對象,讓其擁有和原始event一樣的屬性和值 533 fix: function(event) { 534 if (event[jQuery.expando]) { 535 return event; 536 } 537 538 var i, prop, copy, 539 type = event.type, 540 originalEvent = event, 541 fixHook = this.fixHooks[type]; 542 543 // 如果fixHook不存在判斷是鼠標事件還是鍵盤事件再指向相應的鉤子對象 544 if (!fixHook) { 545 this.fixHooks[type] = fixHook = 546 rmouseEvent.test(type) ? this.mouseHooks : 547 rkeyEvent.test(type) ? this.keyHooks : {}; 548 } 549 // fixHook是否有props屬性,該值是一個數組,如果有則添加到jQuery.event.props中 550 copy = fixHook.props ? this.props.concat(fixHook.props) : this.props; 551 // 創建一個jQuery Event實例event,默認行為和冒泡fix 552 event = new jQuery.Event(originalEvent); 553 554 // 給jq event添加原始event對象的屬性 555 i = copy.length; 556 while (i--) { 557 prop = copy[i]; 558 event[prop] = originalEvent[prop]; 559 } 560 561 // Support: IE<9 562 if (!event.target) { 563 event.target = originalEvent.srcElement || document; 564 } 565 566 // Support: Chrome 23+, Safari? 567 if (event.target.nodeType === 3) { 568 event.target = event.target.parentNode; 569 } 570 571 // Support: IE<9 572 event.metaKey = !! event.metaKey; 573 574 // 如果鉤子對象有filter解決兼容方法,則返回filter后的event 575 return fixHook.filter ? fixHook.filter(event, originalEvent) : event; 576 }, 577 // event對象相關屬性 578 props: 'altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which'.split(' '), 579 // 后續要用的 580 fixHooks: {}, 581 // keyEvent鉤子 582 keyHooks: { 583 props: 'char charCode key keyCode'.split(' '), 584 filter: function(event, original) { 585 if (event.which == null) { 586 event.which = original.charCode != null ? original.charCode : original.keyCode; 587 } 588 589 return event; 590 } 591 }, 592 /* 593 mouseEvent鉤子,處理有關鼠標事件的兼容性. 594 original為原始event對象,event則為jQuery的event對象 595 */ 596 mouseHooks: { 597 props: 'button buttons clientX clientY fromElement offsetX offsetY pageX pageY scrennX screenY toElement'.split(' '), 598 filter: function(event, original) { 599 var body, eventDoc, doc, 600 button = original.button, 601 fromElement = original.fromElement; 602 603 if (event.pageX == null && original.clientX != null) { 604 eventDoc = event.target.ownerDocument || document; 605 doc = eventDoc.documentElement; 606 body = eventDoc.body; 607 608 event.pageX = original.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); 609 event.pageY = original.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); 610 } 611 612 if (!event.relatedTarget && fromElement) { 613 event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; 614 } 615 616 // 為點擊事件添加which屬性, 1 === left;2 === middle; 3 === right 617 // 這里沒使用button作為屬性 618 if (!event.which && button !== undefined) { 619 event.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0))); 620 } 621 622 return event; 623 } 624 }, 625 /* 626 用來處理各事件里的特殊例子 627 */ 628 special: { 629 load: { 630 // 阻止image的load事件冒泡到window.load 631 noBubble: true 632 }, 633 click: { 634 // For checkbox, fire native event so checked state will be right 635 trigger: function() { 636 if (jQuery.nodeName(this, 'input') && this.type === 'checkbox' && this.click) { 637 this.click(); 638 return false; 639 } 640 } 641 }, 642 focus: { 643 trigger: function() { 644 if (this !== document.activeElement && this.focus) { 645 try { 646 this.focus(); 647 return false; 648 } catch (e) {} 649 } 650 }, 651 delegateType: 'focusin' 652 }, 653 blur: { 654 trigger: function() { 655 if (this === document.activeElement && this.blur) { 656 this.blur(); 657 return false; 658 } 659 }, 660 delegateType: 'focusout' 661 }, 662 beforeunload: { 663 postDispatch: function(event) { 664 // Even when returnValue equals to undefined Firefox will still show alert 665 if (event.result !== undefined) { 666 event.originalEvent.returnValue = event.result; 667 } 668 } 669 } 670 }, 671 // 模擬一個event 672 simulate: function(type, elem, event, bubble) { 673 var e = jQuery.extend(new jQuery.Event(), 674 event, { 675 type: type, 676 isSimulated: true, 677 originalEvent: {} 678 }); 679 if (bubble) { 680 jQuery.event.trigger(e, null, elem); 681 } else { 682 jQuery.event.dispatch.call(elem, e); 683 } 684 if (e.isDefaultPrevented()) { 685 event.preventDefault(); 686 } 687 } 688 }; 689 690 // 跨瀏覽器刪除事件 691 jQuery.removeEvent = document.removeEventListener ? 692 function(elem, type, handle) { 693 if (elem.removeEventListener) { 694 elem.removeEventListener(type, handle, false); 695 } 696 } : 697 function(elem, type, handle) { 698 var name = 'on' + type; 699 700 if (elem.detachEvent) { 701 if (typeof elem[name] === core_strundefined) { 702 elem[name] = null; 703 } 704 705 elem.detachEvent(name, handle); 706 } 707 }; 708 709 /* 710 Event類用來解決阻止默認行為和事件冒泡兼容的類,src為原始event對象,props則是event的一些預配置項 711 */ 712 jQuery.Event = function(src, props) { 713 if (!(this instanceof jQuery.Event)) { 714 return new jQuery.Event(src, props); 715 } 716 717 if (src && src.type) { 718 this.originalEvent = src; 719 this.type = src.type; 720 721 this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; 722 } else { 723 this.type = src; 724 } 725 726 if (props) { 727 jQuery.extend(this, props); 728 } 729 730 this.timeStamp = src && src.timeStamp || jQuery.now(); 731 732 this[jQuery.expando] = true; 733 }; 734 735 jQuery.Event.prototype = { 736 isDefaultPrevented: returnFalse, 737 isPropagationStopped: returnFalse, 738 isImmediatePropagationStopped: returnFalse, 739 740 preventDefault: function() { 741 var e = this.originalEvent; 742 743 this.isDefaultPrevented = returnTrue; 744 if (!e) { 745 return; 746 } 747 748 if (e.preventDefault) { 749 e.preventDefault(); 750 } else { 751 e.returnValue = false; 752 } 753 }, 754 stopPropagation: function() { 755 var e = this.originalEvent; 756 757 this.isPropagationStopped = returnTrue; 758 if (!e) { 759 return; 760 } 761 if (e.stopPropagation) { 762 e.stopPropagation(); 763 } 764 e.cancelBubble = true; 765 }, 766 stopImmediatePropagation: function() { 767 this.isImmediatePropagationStopped = returnTrue; 768 this.stopPropagation(); 769 } 770 }; 771 772 jQuery.each({ 773 mouseenter: 'mouseover', 774 mouseleave: 'mouseout' 775 }, function(orig, fix) { 776 jQuery.event.special[orig] = { 777 delegateType: fix, 778 bindType: fix, 779 780 handle: function(event) { 781 var ret, 782 target = this, 783 related = event.relatedTarget, 784 handleObj = event.handleObj; 785 786 // For mousenter/leave call the handler if related is outside the target. 787 // NB: No relatedTarget if the mouse left/entered the browser window 788 // 確保相關元素是在目標元素的外面, 789 // 沒有相關元素指的是移到/移出瀏覽器外 790 if (!related || (related !== target && !jQuery.contains(target, related))) { 791 event.type = handleObj.origType; 792 ret = handleObj.handler.apply(this, arguments); 793 event.type = fix; 794 } 795 return ret; 796 } 797 }; 798 }); 799 800 // IE submit 委托 801 if (!jQuery.support.submitBubbles) { 802 jQuery.event.special.submit = { 803 setup: function() { 804 if (jQuery.nodeName(this, 'form')) { 805 return false; 806 } 807 808 // Lazy-add a submit handler when a descendant form may potentially be submitted 809 jQuery.event.add(this, 'click._submit keypress._submit', function(e) { 810 // Node name check avoids a VML-related crash in IE (#9807) 811 var elem = e.target, 812 form = jQuery.nodeName(elem, 'input') || jQuery.nodeName(elem, 'button') ? elem.form : undefined; 813 if (form && !jQuery._data(form, 'submitBubbles')) { 814 jQuery.event.add(form, 'submit._submit', function(event) { 815 event._submit_bubble = true; 816 }); 817 jQuery._data(form, 'submitBubbles', true); 818 } 819 }); 820 // return undefined since we don't need an event listener 821 }, 822 postDispatch: function(event) { 823 // If form was submitted by the user, bubble the event up the tree 824 if (event._submit_bubble) { 825 delete event._submit_bubble; 826 if (this.parentNode && !event.isTrigger) { 827 jQuery.event.simulate('submit', this.parentNode, event, true); 828 } 829 } 830 }, 831 teardown: function() { 832 // Only need this for delegated form submit events 833 if (jQuery.nodeName(this, 'form')) { 834 return false; 835 } 836 837 // Remove delegated handlers; cleanData eventually reaps submit handlers attached above 838 jQuery.event.remove(this, '._submit'); 839 } 840 }; 841 } 842 843 // IE change delegation and checkbox/radio fix 844 if (!jQuery.support.changeBubbles) { 845 846 jQuery.event.special.change = { 847 848 setup: function() { 849 850 if (rformElems.test(this.nodeName)) { 851 // IE doesn't fire change on a check/radio until blur; trigger it on click 852 // after a propertychange. Eat the blur-change in special.change.handle. 853 // This still fires onchange a second time for check/radio after blur. 854 if (this.type === "checkbox" || this.type === "radio") { 855 jQuery.event.add(this, "propertychange._change", function(event) { 856 if (event.originalEvent.propertyName === "checked") { 857 this._just_changed = true; 858 } 859 }); 860 jQuery.event.add(this, "click._change", function(event) { 861 if (this._just_changed && !event.isTrigger) { 862 this._just_changed = false; 863 } 864 // Allow triggered, simulated change events (#11500) 865 jQuery.event.simulate("change", this, event, true); 866 }); 867 } 868 return false; 869 } 870 // Delegated event; lazy-add a change handler on descendant inputs 871 jQuery.event.add(this, "beforeactivate._change", function(e) { 872 var elem = e.target; 873 874 if (rformElems.test(elem.nodeName) && !jQuery._data(elem, "changeBubbles")) { 875 jQuery.event.add(elem, "change._change", function(event) { 876 if (this.parentNode && !event.isSimulated && !event.isTrigger) { 877 jQuery.event.simulate("change", this.parentNode, event, true); 878 } 879 }); 880 jQuery._data(elem, "changeBubbles", true); 881 } 882 }); 883 }, 884 885 handle: function(event) { 886 var elem = event.target; 887 888 // Swallow native change events from checkbox/radio, we already triggered them above 889 if (this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox")) { 890 return event.handleObj.handler.apply(this, arguments); 891 } 892 }, 893 894 teardown: function() { 895 jQuery.event.remove(this, "._change"); 896 897 return !rformElems.test(this.nodeName); 898 } 899 }; 900 } 901 902 // Create "bubbling" focus and blur events 903 if (!jQuery.support.focusinBubbles) { 904 jQuery.each({ 905 focus: "focusin", 906 blur: "focusout" 907 }, function(orig, fix) { 908 909 // Attach a single capturing handler while someone wants focusin/focusout 910 var attaches = 0, 911 handler = function(event) { 912 jQuery.event.simulate(fix, event.target, jQuery.event.fix(event), true); 913 }; 914 915 jQuery.event.special[fix] = { 916 setup: function() { 917 if (attaches++ === 0) { 918 document.addEventListener(orig, handler, true); 919 } 920 }, 921 teardown: function() { 922 if (--attaches === 0) { 923 document.removeEventListener(orig, handler, true); 924 } 925 } 926 }; 927 }); 928 } 929 930 jQuery.fn.extend({ 931 on: function(types, selector, data, fn, /*INTERNAL*/ one) { 932 var type, origFn; 933 934 // 添加多個事件注冊 935 if (typeof types === "object") { 936 // ( types-Object, selector, data ) 937 if (typeof selector !== "string") { 938 // ( types-Object, data ) 939 data = data || selector; 940 selector = undefined; 941 } 942 // 為每個事件迭代 943 for (type in types) { 944 this.on(type, selector, data, types[type], one); 945 } 946 return this; 947 } 948 949 // 如果data和fn都為空,則將selector賦值給fn, 950 if (data == null && fn == null) { 951 // ( types, fn ) 952 fn = selector; 953 data = selector = undefined; 954 } else if (fn == null) { 955 if (typeof selector === "string") { 956 // ( types, selector, fn ) 957 fn = data; 958 data = undefined; 959 } else { 960 // ( types, data, fn ) 961 fn = data; 962 data = selector; 963 selector = undefined; 964 } 965 } 966 if (fn === false) { 967 fn = returnFalse; 968 } else if (!fn) { 969 return this; 970 } 971 972 // 如果只是一次性事件,則將fn從新包裝 973 if (one === 1) { 974 origFn = fn; 975 fn = function(event) { 976 // 這里使用空的jq對象來解除事件綁定信息, 977 // 具體定位是通過event.handleObj和目標元素event.delegateTarget 978 jQuery().off(event); 979 // 執行原始的fn函數 980 return origFn.apply(this, arguments); 981 }; 982 // Use same guid so caller can remove using origFn 983 // 備忘信息 984 fn.guid = origFn.guid || (origFn.guid = jQuery.guid++); 985 } 986 // 統一調用jQuery.event.add方法添加事件處理 987 return this.each(function() { 988 jQuery.event.add(this, types, fn, data, selector); 989 }); 990 }, 991 one: function(types, selector, data, fn) { 992 return this.on(types, selector, data, fn, 1); 993 }, 994 off: function(types, selector, fn) { 995 var handleObj, type; 996 // 當傳遞的types是jQuery創建的event對象時 997 if (types && types.preventDefault && types.handleObj) { 998 // ( event ) dispatched jQuery.Event 999 handleObj = types.handleObj; 1000 jQuery(types.delegateTarget).off( 1001 handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, 1002 handleObj.selector, 1003 handleObj.handler 1004 ); 1005 return this; 1006 } 1007 // 當types是對象,遍歷遞歸 1008 if (typeof types === "object") { 1009 // ( types-object [, selector] ) 1010 for (type in types) { 1011 this.off(type, selector, types[type]); 1012 } 1013 return this; 1014 } 1015 if (selector === false || typeof selector === "function") { 1016 // ( types [, fn] ) 1017 fn = selector; 1018 selector = undefined; 1019 } 1020 if (fn === false) { 1021 fn = returnFalse; 1022 } 1023 // 統一調用jQuery.event.remove移除事件處理程序及相關信息 1024 return this.each(function() { 1025 jQuery.event.remove(this, types, fn, selector); 1026 }); 1027 }, 1028 bind: function(types, data, fn) { 1029 return this.on(types, null, data, fn); 1030 }, 1031 unbind: function(types, fn) { 1032 return this.off(types, null, fn); 1033 }, 1034 delegate: function(selector, types, data, fn) { 1035 return this.on(types, selector, data, fn); 1036 }, 1037 undelegate: function(selector, types, fn) { 1038 // ( namespace ) or ( selector, types [, fn] ) 1039 return arguments.length === 1 ? this.off(selector, "**") : this.off(types, selector || "**", fn); 1040 }, 1041 trigger: function(type, data) { 1042 return this.each(function() { 1043 jQuery.event.trigger(type, data, this); 1044 }); 1045 }, 1046 triggerHandler: function(type, data) { 1047 var elem = this[0]; 1048 if (elem) { 1049 return jQuery.event.trigger(type, data, elem, true); 1050 } 1051 } 1052 });

?

轉載于:https://www.cnblogs.com/webFrontDev/p/3337634.html

總結

以上是生活随笔為你收集整理的jQuery1.9.1源码分析--Events模块的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。