1. 接下来就是触发事件了。事件触发后的处理函数的分发主要靠两个函数,一个jQuery.event.dispatch,一个是jQuery.event.handlers。这个dispatch会调用handlers,而handlers会返回一个数组,这个数组是符合本次事件条件的所有处理函数对象。dispatch只管执行。
    那这个handlers是如何运作的呢。绑定在一个元素上面的非代理事件是肯定要被触发的,所以会全数被返回。主要是代理事件的筛选,jQuery会从触发了事件(target所指的元素)的元素一级一级的往上检查selector是否符合,符合就把它返回。这种策略和元素的事件代理会有所不同,看下面代码:

    <!doctype html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Event fun</title>
    </head>
    <body>
    <div style="width:200px;height:600px;background-color: red;">
    <div style="width:200px;height:500px;background-color: blue;">
    <div style="width:200px;height:400px;background-color: green;">
    <div id="a" style="width:200px;height:300px;background-color: black;">
    <div style="width:200px;height:200px;background-color: yellow;"> </div>
    </div>
    </div>
    </div>
    </div>
    <script src="../../jquery-2.1.1.js"></script>
    <script>
    $(document).on('click',function(){})
    $(document).on('click','#a',function(){})
    $(document).on('click','div',{
    name:'qq',
    age:'dd'
    },function(){
    console.log(1);//5次
    })
    var doc = $(document)
    document.onclick = function(e){
    console.log(1);//1次
    }
    </script> </body>
    </html>

    在这个有5层的html嵌套结构中,当在最内部的div上点击一下。jQuery的事件代理会触发5次。元素的会触发1次。
    上源码:

    handlers: function( event, handlers ) {//新的事件对象   ,  该类型的监听对象数组。将当前元素的所有监听事件排成一个序列,从底到顶,然后是普通事件
    var i, matches, sel, handleObj,
    handlerQueue = [],//响应对象数组
    delegateCount = handlers.delegateCount,//代理事件的数量
    cur = event.target;//目标元素 // Find delegate handlers
    // Black-hole SVG <use> instance trees (#13180)
    // Avoid non-left-click bubbling in Firefox (#3861)
    if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { for ( ; cur !== this; cur = cur.parentNode || this ) {//从触发了事件的目标元素,向上找,一直到代理的元素 // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
    if ( cur.disabled !== true || event.type !== "click" ) {//排除不支持click的元素
    matches = [];
    for ( i = 0; i < delegateCount; i++ ) {
    handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203)
    sel = handleObj.selector + " "; if ( matches[ sel ] === undefined ) {//判断目标元素是否匹配selector的过滤
    matches[ sel ] = handleObj.needsContext ?
    jQuery( sel, this ).index( cur ) >= 0 :
    jQuery.find( sel, this, null, [ cur ] ).length;
    }
    if ( matches[ sel ] ) {//匹配的话将响应对象入队。
    matches.push( handleObj );
    }
    }
    if ( matches.length ) {//如果匹配,将元素和响应对象序列入数组
    handlerQueue.push({ elem: cur, handlers: matches });
    }
    }
    }
    } // Add the remaining (directly-bound) handlers
    if ( delegateCount < handlers.length ) {//绑定了普通的事件,入数组 。
    handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
    } return handlerQueue;
    },

    接下来就是这个dispatch了,其实也很简单,就干了3样事情。
    1、检查是不是特殊的事件,如果是优先使用特殊的处理函数
    2、调用函数传入jQuery事件对象。这就是为什么在处理函数内部的事件对象是jQuery的事件对象了。
    3、检查上函数的返回值是不是为false,如果是就调用jQuery.Event对象的原型中的方法阻止冒泡和默认行为。
    上源码:

    dispatch: function( event ) {//event为原生事件对象  函数作用:主监听函数
    
            // Make a writable jQuery.Event from the native event object
    event = jQuery.event.fix( event );//创建事件对象 var i, j, ret, matched, handleObj,//ret返回值,matched放置匹配过的响应对象
    handlerQueue = [],//待执行队列。包括后代元素匹配的代理监听对象数组 和 当前元素上绑定的普通监听对象数组。
    args = slice.call( arguments ),//把arguments转换成真正的数组
    handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],//当前事件类型对应的监听对象数组
    special = jQuery.event.special[ event.type ] || {};//获取事件的修正对象 // Use the fix-ed jQuery.Event rather than the (read-only) native event
    args[0] = event;//存储事件对象
    event.delegateTarget = this;//代理对象 // Call the preDispatch hook for the mapped type, and let it bail if desired
    if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
    return;
    } // Determine handlers
    handlerQueue = jQuery.event.handlers.call( this, event, handlers );//确定要执行的响应函数数组 // Run delegates first; they may want to stop propagation beneath us
    i = 0;
    while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {//队列中还有元素且没有被阻止冒泡
    event.currentTarget = matched.elem;//当前的元素 j = 0;
    while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {//该元素上有响应对象且没有被阻止 // Triggered event must either 1) have no namespace, or
    // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
    if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {//没有传入命名空间,或者命名空间匹配 event.handleObj = handleObj;//复制
    event.data = handleObj.data; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
    .apply( matched.elem, args );//优先调用修正事件处理函数,将返回值存在 if ( ret !== undefined ) {//返回值为false是,阻止冒泡和默认行为
    if ( (event.result = ret) === false ) {
    event.preventDefault();
    event.stopPropagation();
    }
    }
    }
    }
    } // Call the postDispatch hook for the mapped type
    if ( special.postDispatch ) {//beforeUnload
    special.postDispatch.call( this, event );
    } return event.result;
    },
  2. 下面到了接触事件绑定。jQuery中删除一个事件监听函数实际上就是从事件处理函数对象的数组中删除掉一个元素而已。公开的API是off,这个函数是修正参数的,然后在底层调用jQuery.event.remove来删除。可以一次过多个事件函数,因为off函数会递归的调用自己。
    jQuery.event.remove函数主要遍历处理函数对象数组,然后检查一下条件
    1、是否有传type参数,如果有,是否和当前处理函数对象的type是否相等
    2、是否有传处理函数,如果有,和绑定的时候是否为同一个;
    3、是否有传命名空间参数,如果有,是否和处理函数对象的命名空间是否相等
    4、是否有传selector参数,如果有,是否和处理函数对象的selector是否相等
    如果上面的条件都符合,就把当前的处理函数对象删除。

    上源码

    off: function( types, selector, fn ) {
    var handleObj, type;
    if ( types && types.preventDefault && types.handleObj ) {//使用dispatched分发过的jquery处理函数对象,也就是事件正在被触发
    // ( event ) dispatched jQuery.Event
    handleObj = types.handleObj;
    jQuery( types.delegateTarget ).off(
    handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
    handleObj.selector,
    handleObj.handler
    );
    return this;
    }
    if ( typeof types === "object" ) {//types 是对象,用于一次性移除多个事件类型和过个监听函数
    // ( types-object [, selector] )
    for ( type in types ) {
    this.off( type, selector, types[ type ] );
    }
    return this;
    }
    if ( selector === false || typeof selector === "function" ) {//修正参数,selector为false,或者只传入两个参数
    // ( types [, fn] )
    fn = selector;
    selector = undefined;
    }
    if ( fn === false ) {//没传fn
    fn = returnFalse;
    }
    return this.each(function() {//调用remove删除事件
    jQuery.event.remove( this, types, fn, selector );
    });
    }, remove: function( elem, types, handler, selector, mappedTypes ) { var j, origCount, tmp,
    events, t, handleObj,
    special, handlers, type, namespaces, origType,
    elemData = data_priv.hasData( elem ) && data_priv.get( elem ); if ( !elemData || !(events = elemData.events) ) {//没有关联的缓存数据或者事件缓存对象
    return;
    } // Once for each type.namespace in types; type may be omitted
    types = ( types || "" ).match( rnotwhite ) || [ "" ];//转换成数组
    t = types.length;
    while ( t-- ) {
    tmp = rtypenamespace.exec( types[t] ) || [];
    type = origType = tmp[1];
    namespaces = ( tmp[2] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element
    if ( !type ) {//如果没有指定事件类型,则移除元素所有事件或命名空间中所有事件(有给定命名空间的情况)
    for ( type in events ) {
    jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
    }
    continue;
    } special = jQuery.event.special[ type ] || {};//获得修正对象(如果有)
    type = ( selector ? special.delegateType : special.bindType ) || type;//如果有传selector则修正为代理事件,否则优先考虑修正为更好的事件
    handlers = events[ type ] || [];
    tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );//用于检测已绑定事件和types的命名空间是否一样 // Remove matching events
    origCount = j = handlers.length;
    while ( j-- ) {
    handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) &&//mappedTypes不为真时比较传入类型和监听对象的原始事件类型
    ( !handler || handler.guid === handleObj.guid ) &&//没有指定监听函数 或 指定监听函数与监听对象具有一样的id
    ( !tmp || tmp.test( handleObj.namespace ) ) &&//没有指定命名空间或者监听对象的命名空间具有指定的命名空间
    ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {//没传入selector 或 有传入但是与监听对象的相等 或 为"**"(所有)是监听对象有selector
    handlers.splice( j, 1 );//从监听对象数组中删除 if ( handleObj.selector ) {//如果删除了的是代理事件 则修正dele gateCount 以便下一次插入代理事件的正确
    handlers.delegateCount--;
    }
    if ( special.remove ) {//有对应的修正方法remove,则调用
    special.remove.call( elem, handleObj );
    }
    }
    } // Remove generic event handler if we removed something and no more handlers exist
    // (avoids potential for endless recursion during removal of special event handlers)
    if ( origCount && !handlers.length ) {//某类型事件监听对象数组被清空,删除主监听函数
    if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {优先调用teardown移除主监听函数
    jQuery.removeEvent( elem, type, elemData.handle );
    } delete events[ type ];//从总监听对象数组中删除该类型的监听对象数组
    }
    } // Remove the expando if it's no longer used
    if ( jQuery.isEmptyObject( events ) ) {//总监听对象数组为空,说明该元素上的所有事件都被移除
    delete elemData.handle;//移除主监听函数储存数据的对象
    data_priv.remove( elem, "events" );//移除缓存
    }
    },

jQuery源码分析--Event模块(2)的更多相关文章

  1. jQuery源码分析--Event模块(1)

    jQuery的Event模块提供了强大的功能:事件代理,自定义事件,自定义数据等.今天记录一下它实现的原理. 我们都知道,在js的原生事件中,有事件对象和回调函数这两样东西.但是事件对象是只读的,所以 ...

  2. jQuery源码分析--Event模块(3)

    最后剩下了事件的手动触发了.jQuery提供了两个函数trigger和triggerHandler来手动触发事件,可以触发原生事件和自定义的事件.这个触发不单只会触发有jQuery绑定事件,而且也会触 ...

  3. Zepto源码分析-event模块

    源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT l ...

  4. nginx源码分析——event模块

    源码:nginx 1.12.0   一.简介      nginx是一款非常受欢迎的软件,具备高性能.模块化可定制的良好特性.之前写了一篇nginx的http模块分析的文章,主要对http处理模块进行 ...

  5. zepto源码分析·event模块

    准备知识 事件的本质就是发布/订阅模式,dom事件也不例外:先简单说明下发布/订阅模式,dom事件api和兼容性 发布/订阅模式 所谓发布/订阅模式,用一个形象的比喻就是买房的人订阅楼房消息,售楼处发 ...

  6. 读Zepto源码之Event模块

    Event 模块是 Zepto 必备的模块之一,由于对 Event Api 不太熟,Event 对象也比较复杂,所以乍一看 Event 模块的源码,有点懵,细看下去,其实也不太复杂. 读Zepto源码 ...

  7. [转] jQuery源码分析-如何做jQuery源码分析

    jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习. 当我读到难度的书或者源码时,会和<如何阅读一本书> ...

  8. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  9. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

随机推荐

  1. MySQL 密码设置

    如何修改 MySQL 密码: [root@localhost ~]$ mysqladmin -uroot password 'newPass' # 在无密码的情况下设置密码 [root@localho ...

  2. ARM漏洞

    Google安全团队Project Zero公布了多个高危漏洞,称这些漏洞几乎影响到了市面上所有的微处理器,AMD.ARM还是英特尔的处理器都难以幸免,围绕这些处理器打造的操作系统和硬件设备也会受到影 ...

  3. vue中的小踩坑(01)

    前言: 昨天算是使用vue2.0+element-ui做了一点小小的页面,可是源于其中遇到的问题,特地整理一下,以防自己还有其他的小伙伴们继续踩坑. 过程:         1.不知道大家有没有注意到 ...

  4. Set集合遍历方式

    for(String str : set) { System.out.println(str); } for (Iterator iter = set.iterator(); iter.hasNext ...

  5. vux报错 this指针问题

    报错: 代码: 结果:取不到this.tishi,不明白为什么在请求内部会取不到这个值. 分析:this可能有问题 解决: 结果: 这样就能取到this.tishi的值了. 总结: methods:{ ...

  6. php中关于时间的用法

    一.时间戳相关:        当前时间戳:time();         把时间戳转换为时间显示:date("Y-m-d H:i:s", $a);         把日期时间转换 ...

  7. 【黑金原创教程】【Modelsim】Modelsim原创教程连载导读【连载完成,共六章】

    [第一章]Modelsim仿真的扫盲文 [第二章]Modelsim就是电视机 [第三章]理想就是美丽 [第四章]激励文本就是仿真环境 [第五章]仿真就是人生 [第六章]结束就是开始

  8. bond的操作方式

    本文转自网上,版权归属原作者,原文地址 :https://www.cnblogs.com/5201351/p/4898342.html 操作系统:CentOS Linux release 7.1.15 ...

  9. 【BZOJ1645】[Usaco2007 Open]City Horizon 城市地平线 离散化+线段树

    [BZOJ1645][Usaco2007 Open]City Horizon 城市地平线 Description Farmer John has taken his cows on a trip to ...

  10. 【Android】Android背景选择器selector用法汇总

    一.创建xml文件,位置:drawable/xxx.xml,同目录下记得要放相关图片 <?xml version="1.0" encoding="utf-8&quo ...