• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
しμCīfeγ
   所以者何? 须菩提,过去心不可得,现在心不可得,未来心不可得.
博客园    首页    新随笔    联系   管理    订阅  订阅

jQuery事件

jQuery 事件 --- jQuery.event.trigger

这一部分在 jQuery 的事件系统里主要负责手动触发事件,包括浏览器默认行为、自定义的事件和 dom 上的 on 事件;

由于 jQuery 的事件系统基于数据缓存系统,所以这里需要自己来模拟事件的冒泡机制;

手动触发事件时的流程:

 

主要的方法包括.trigger()和.triggerHandler(),它们都基于内部方法 jQuery.event.trigger 来实现: 

trigger: function( type, data ) {
    return this.each( function() {
        jQuery.event.trigger( type, data, this );
    } );
},
triggerHandler: function( type, data ) {
    if ( this[0] ) {
        return jQuery.event.trigger( type, data, this[0], true );
    }
},

 

这两个外部 API 方法主要有两个区别:

  1)trigger方法会触发绑定的事件和浏览器的默认行为,而triggerHandler方法则不会触发浏览器的默认行为

  2)triggerHandler方法最后会返回处理函数的返回值,而trigger则返回 jQuery 对象,所以triggerHandler是无法进行链式调用的,并且可以把它作为普通的 js 方法来使用,如:

marginTop = parseInt( sidebar.triggerHandler( "getTop", [config.id] ),10 ) - 1 + "px"

 

     3)triggerHandler方法只会影响第一个匹配的元素

   4)triggerHandler方法不会冒泡,也就是非直接由目标元素触发的时候,这个方法什么也不做

 

源码解析

trigger: function( event, data, elem, onlyHandlers ) {
    // Don't do events on text and comment nodes
    // 老规矩,刨除掉注释和文本节点
    if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
        return;
    }

    // Event object or event type
    // cache: jQuery.cache
    // exclusive: 是否只触发没有命名空间的事件
    // cur: 当前元素的祖先元素
    // old: 最顶层元素
    // ontype: 行内监听事件
    // special: 事件修正对象
    // handle: 监听函数
    // eventPath: 冒泡路径数组
    // bubbleType: 当前事件类型对应的冒泡事件类型
    // type: 事件类型
    var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,
        type = event.type || event,// event 可能是自定义事件对象或自定义的对象
        namespaces = [];// 命名空间数组

    // focus/blur morphs to focusin/out; ensure we're not firing them right now
    // 如果正在触发focus/blur的浏览器默认行为,这里要先保证不触发它们
    if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
        return;
    }

    if ( type.indexOf( "!" ) >= 0 ) {
        // Exclusive events trigger only for the exact event (no namespaces)
        // 只会执行没有命名空间的监听函数
        type = type.slice( 0, -1 );
        exclusive = true;
    }

    if ( type.indexOf( "." ) >= 0 ) {
        // Namespaced trigger; create a regexp to match event type in handle()
        // 这里为什么要排序?
        namespaces = type.split( "." );
        type = namespaces.shift();
        namespaces.sort();
    }

    if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
        // No jQuery handlers for this event type, and it can't have inline handlers
        // jQuery.event.customEvent的用处不明
        return;
    }

    /*if ( typeof event === "object" ) {
        event = event[ jQuery.expando ] ? event : new jQuery.Event( type, event );
    } else {
        event = new jQuery.Event( type )
    }*/

    // Caller can pass in an Event, Object, or just an event type string
    event = typeof event === "object" ?
        // jQuery.Event object
        event[ jQuery.expando ] ? event : 
            // Object literal
            new jQuery.Event( type, event ) :
        // Just the event type (string)
        new jQuery.Event( type );

    event.type = type;// 统一修正为不带命名空间的事件类型
    event.isTrigger = true;
    event.exclusive = exclusive;
    event.namespace = namespaces.join( "." );
    event.namespace_re = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : null;// 命名空间匹配
    ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";// 忽略IE的table元素带冒号的属性访问

    // Handle a global trigger
    // 全局触发
    // 但这里为了防止浏览器行为的混乱,只触发了事件的监听函数,不触发默认行为
    if ( !elem ) {

        // TODO: Stop taunting the data cache; remove global events and always attach to document
        cache = jQuery.cache;
        for ( i in cache ) {
            if ( cache[ i ].events && cache[ i ].events[ type ] ) {
                jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
            }
        }
        return;
    }

    // Clean up the event in case it is being reused
    // 把返回的数据重置,以便重新使用
    event.result = undefined;
    if ( !event.target ) {
        event.target = elem;// 如果事件的 target 属性不存在,则让它指向当前的元素
    }

    // Clone any incoming data and prepend the event, creating the handler arg list
    // 把 event 和 data 都处理成数组
    data = data != null ? jQuery.makeArray( data ) : [];
    data.unshift( event );

    // Allow special events to draw outside the lines
    // special.trigger是一个预留的方法,类似于special.preDispatch
    special = jQuery.event.special[ type ] || {};
    if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
        return;
    }

    // Determine event propagation path in advance, per W3C events spec (#9951)
    // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
    // 这里开始构造冒泡路径
    eventPath = [
        [ elem, special.bindType || type ]
    ];
    // onlyHandlers为 true 
    // special.noBubble为true( load、scroll、error 方法 )
    // 当前是 window 元素
    if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {

        bubbleType = special.delegateType || type;// 修正冒泡类型
        cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;// foucs/blur后面单独处理,其它一律复制为父元素
        for ( old = elem; cur; cur = cur.parentNode ) {
            eventPath.push( [ cur, bubbleType ] );
            old = cur;// cur 最终会变为 undefined
        }

        // Only add window if we got to document (e.g., not plain obj or detached DOM)
        // 如果 old 是 document 对象,则按照事件规范向eventPath中添加 window 对象
        if ( old === (elem.ownerDocument || document) ) {
            eventPath.push( [ old.defaultView || old.parentWindow || window, bubbleType ] );
        }
    }

    // Fire handlers on the event path
    // 路径组装完成,开始触发
    for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {

        cur = eventPath[i][0];// 元素
        event.type = eventPath[i][1];// 事件类型

        // 这里是在检验路径上的元素是否绑定过该类型的事件
        // 如果有,则取出主监听函数
        handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
        if ( handle ) {
            handle.apply( cur, data );
        }
        // Note that this is a bare JS function and not a jQuery handler
        // 这里为什么要检测 acceptData?
        handle = ontype && cur[ ontype ];
        if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
            event.preventDefault();
        }
    }
    event.type = type;// 主监听函数可能会修改 type,所以这里进行了修正

    // If nobody prevented the default action, do it now
    // 接下来开始触发默认行为
    // 肯定是不能在 a 元素上触发默认点击行为的,因为可能会跳转
    if ( !onlyHandlers && !event.isDefaultPrevented() ) {
        // 这里的special._default同样是一个预留的方法
        if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {

            // Call a native DOM method on the target with the same name name as the event.
            // 通过事件类型同名函数的方式来触发元素的默认行为
            // Can't use an .isFunction() check here because IE6/7 fails that test.
            // Don't do default actions on window, that's where global variables be (#6170)
            // IE<9 dies on focus/blur to hidden element (#1486)
            // IE9以下不在隐藏元素上触发focus/blur事件
            if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {

                // Don't re-trigger an onFOO event when we call its FOO() method
                // 这里借了 old 这个变量来存储元素的行内事件
                old = elem[ ontype ];

                if ( old ) {
                    // 防止行内事件再被触发
                    elem[ ontype ] = null;
                }

                // Prevent re-triggering of the same event, since we already bubbled it above
                jQuery.event.triggered = type;
                elem[ type ]();// 触发默认行为
                jQuery.event.triggered = undefined;

                if ( old ) {
                    elem[ ontype ] = old;// 把行内事件恢复回来
                }
            }
        }
    }

    return event.result;
}

 

jQuery.event.dispatch相关片段

customEvent

customEvent: {
    "getData": true,
    "setData": true,
    "changeData": true
}

 

jQuery.event.add相关片段

// Keep track of which events have ever been used, for event optimization
jQuery.event.global[ type ] = true;

 

jQuery.Event()构造函数

jQuery.Event = function( src, props ) {
    // Allow instantiation without the 'new' keyword
    if ( !(this instanceof jQuery.Event) ) {
        return new jQuery.Event( src, props );
    }

    // Event object
    if ( src && src.type ) {
        this.originalEvent = src;
        this.type = src.type;

        // Events bubbling up the document may have been marked as prevented
        // by a handler lower down the tree; reflect the correct value.
        this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
            src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;

        // Event type
    } else {
        this.type = src;
    }

    // Put explicitly provided properties onto the event object
    if ( props ) {
        jQuery.extend( this, props );
    }

    // Create a timestamp if incoming event doesn't have one
    this.timeStamp = src && src.timeStamp || jQuery.now();

    // Mark it as fixed
    this[ jQuery.expando ] = true;
};

 

posted @ 2014-09-23 18:43  しμCīfeγ  阅读(193)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3