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     });

 

你可能感兴趣的:(jquery)