• 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     });
  • 相关阅读:
    文件上传与下载/Mail
    监听器/国际化
    过滤器
    父类转为子类涉及到的安全问题
    连接池
    【MySQL】Windows10下的安装与配置
    【neo4j】关于出现The old parameter syntax `{param}` is no longer supported. Please use `$param` instead的问题
    关于GitHub上传超过100M文件方法
    记录一次在知道创宇公司的实习面试经历
    《机器学习实战(基于scikit-learn和TensorFlow)》第七章内容学习心得
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/3337634.html
Copyright © 2020-2023  润新知