jQuery1.9.1源码分析--Events模块
生活随笔
收集整理的這篇文章主要介紹了
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模块的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: start running 开始跑步减肥
- 下一篇: 用python扩展snmp