• 前端事件系统(四)


    事件派发


    首先来看看jQuery.event的dispatch方法

    dispatch: function( event ) {
    
        // 对event对象进行修正
        event = jQuery.event.fix( event );
    
        var i, j, ret, matched, handleObj,
            handlerQueue = [],
            args = slice.call( arguments ),
            // 读取事件缓存
            handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
            special = jQuery.event.special[ event.type ] || {};
    
        // 将参数一重置为jQuery的事件对象
        args[0] = event;
        // 添加delegate属性,用于事件代理
        event.delegateTarget = this;
    
        if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
            return;
        }
    
        // 取得事件队列
        handlerQueue = jQuery.event.handlers.call( this, event, handlers );
    
        // 对于事件队列的处理
        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 ) {
                        if ( (event.result = ret) === false ) {
                            event.preventDefault();
                            event.stopPropagation();
                        }
                    }
                }
            }
        }
    
        // Call the postDispatch hook for the mapped type
        if ( special.postDispatch ) {
            special.postDispatch.call( this, event );
        }
    
        return event.result;
    },
    

    通读整段代码,归纳一下,jQuery.event.dispatch是做了一下几项处理的

    1. 对event对象进行修复
    2. 读取事件句柄
    3. 取得事件队列并对其进行处理

    先看第一点

    对event对象的修复,这一步得到event对象,是jQuery的event对象,而非原生的对象。这里jQuery将原本只读的对象,变为了一个可读可写的对象,这样就可以对其进行随意操作了。不过对于event对象修复,我打算现将其放到下一章与事件的修复一起进行讲解,因此这里只需要知道,这里是返回的jQuery的event对象就行了。

    第二点也不再多说,就是读取了事件的缓存

    那么来到第三点,也是事件分发的另外一个重点

    事件队列的获取与处理。

    在之前版本的jQuery中,队列的生成与处理,都是放在了dispatch中进行,不过如今队列已经交由jQuery.event.handlers生成并返回,那么我们首先来看下获取到了handlerQueue到底是什么,即也就是对于jQuery.event.handlers来进行阅读。

    jQuery.event.handlers


    handlers: function( event, handlers ) {
        var i, matches, sel, handleObj,
            handlerQueue = [],
            delegateCount = handlers.delegateCount,
            cur = event.target;
    
        // 判断是否是事件代理、与是否是鼠标左键的点击事件
        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)
                // 不对disabled的元素进行click的处理
                if ( cur.disabled !== true || event.type !== "click" ) {
                    // 收集符合条件的事件句柄
                    matches = [];
                    for ( i = 0; i < delegateCount; i++ ) {
                        handleObj = handlers[ i ];
    
                        // 获取selector
                        sel = handleObj.selector + " ";
    
                        if ( matches[ sel ] === undefined ) {
                            // 匹配事件句柄
                            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 });
                    }
                }
            }
        }
    
        // 在事件队列中,增加其他直接绑定的事件句柄
        if ( delegateCount < handlers.length ) {
            handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
        }
    
        return handlerQueue;
    }
    

    这一部分,整体看来比较复杂,我们来理一下一下。

    这部分先对事件代理做了判断并进行了处理,采用match来对符合条件的事件句柄做一个筛选,并将所有符合条件的事件句柄,按从深及浅的顺序,一一放入了事件队列之中。

    然后,在处理完成了事件代理之后,采用delegateCount区分事件代理以及直接绑定,再将直接绑定的事件句柄,放入事件队列之中,生成了最终的事件队列。这样,最终得到的,就是一个委托层次越深,便越会提前执行的事件队列。

    因此,事件委托,在这一步就已经完成了。同时,因为jQuery的事件处理机制,是这样一个队列的形式,因此,之前在第一章末尾所提及的,对于执行顺序的问题,这里也很好的解决了。

    再回来看dispatch对于事件队列的处理


    那么最后,我们来看dispatch中如何对于这个事件队列进行的处理。

    // 对于事件队列的处理
    i = 0;
    while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
        event.currentTarget = matched.elem;
    
        j = 0;
        while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
    
            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 );
    
    			  //直接return false,即可event.preventDefault以及stopPropagation
                if ( ret !== undefined ) {
                    if ( (event.result = ret) === false ) {
                        event.preventDefault();
                        event.stopPropagation();
                    }
                }
            }
        }
    }
    

    这一部对于事件队列进行了有序的执行(由深及浅再到本身),然后,在这个过程中,通过已经修正过的jQuery事件对象,动态的改变event对象的属性,在执行事件句柄。同时,也对return false后,直接调用event.preventDefault(),与event.stopPropagation()进行了处理。

    总结:


    那么,到目前,对于事件绑定这一块,除了对于事件的修复部分,其他的部分都已经阅读完毕。我们到最后再来理一下整个的过程。

    1. 首先,绑定时,采用了on方法,在这个过程中,on对于我们绑定时候,所传进来的参数,一一进行了处理,并最终将其传入jQuery.event.add中来执行。
    2. event.add部分,引用了dispatch方法进行了事件分发,并将事件名与事件句柄进行了填充
    3. dispatch部分,对于event对象进行修复,读取事件句柄,同时取得事件队列并对其进行处理,并执行回调。

    那么下一章,将对jQuery事件对象的修复,以及事件的修复,进行一个讲解。

  • 相关阅读:
    mysql备份还原
    JavaScript位移运算多个大于号的使用方法
    js with 语句的用法
    公告栏文字滚动
    Tar打包、压缩与解压缩到指定目录的方法
    域名跳转汇总文章
    linux常用命令大全
    nginx添加多站点
    linux一键安装php环境
    linux安装unzip及使用
  • 原文地址:https://www.cnblogs.com/observernotes/p/4959526.html
Copyright © 2020-2023  润新知