事件触发jQuery event(下)

最近应用开发的过程中出现了一个小问题,顺便记录一下原因和方法--事件触发

 前文要主分析了添加事件监听的方法,本文则要主讲删除事件监听,以及事件模拟。

 

    

jQuery.fn.off

jQuery.fn.off = function( types, selector, fn ) {
    var handleObj, type;
    // 如果types是象对,其实在现应该说是type,并且具有preventDefalut和handleObj
    if ( types && types.preventDefault && types.handleObj ) {
        // 通过types取获handleObj
        handleObj = types.handleObj;
        // 转成字符串来消取事件
        jQuery( types.delegateTarget ).off(
            handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
            handleObj.selector,
            handleObj.handler
        );
        return this;
    }
    // 如果types还是象对,那么为认其是是一个map,key对应事件名,value对应理处函数
    if ( typeof types === "object" ) {
        // ( types-object [, selector] )
        // 遍历部全type
        for ( type in types ) {
            this.off( type, selector, types[ type ] );
        }
        return this;
    }
    
    // 如果selector为false,或者selector是个函数
    if ( selector === false || typeof selector === "function" ) {
        // ( types [, fn] )
        // 等同于传进来types和fn
        fn = selector;
        selector = undefined;
    }
    if ( fn === false ) {
        // 如果fn是false,则定义为一个return false的函数
        fn = returnFalse;
    }
    // 遍历部全元素
    return this.each(function() {
        // 应用jQuery.event.remove删除部全事件理处
        jQuery.event.remove( this, types, fn, selector );
    });
};

    这个方法逻辑还是比拟清晰的,试尝理处各种传参式方当前,终最都是用利jQuery.event.remove来删除事件理处函数的。

    

 

    

jQuery.event.remove 

jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ) {

    var j, origCount, tmp,
        events, t, handleObj,
        special, handlers, type, namespaces, origType,
        // 通过部内存缓取获象对关相数据
        elemData = jQuery.hasData( elem ) && jQuery._data( elem );

    // 如果没有关相存缓数据,或者存缓中没有关相理处表列,则这个象对没事件可删除
    if ( !elemData || !(events = elemData.events) ) {
        // 退出
        return;
    }

    // types多是通过空格分开的多个type,转成组数
    types = ( types || "" ).match( core_rnotwhite ) || [""];
    t = types.length;
    // 遍历部全type
    while ( t-- ) {
        // 解分type和namespace
        tmp = rtypenamespace.exec( types[t] ) || [];
        // 失掉type
        type = origType = tmp[1];
        // 失掉namespace
        namespaces = ( tmp[2] || "" ).split( "." ).sort();

        // 如果type是undefined,即来本的type是.xxx.xxx之类的命名空间
        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;
        handlers = events[ type ] || [];
        tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );

        // 删掉除足满的事件
        origCount = j = handlers.length;
        while ( j-- ) {
            // 失掉事件象对
            handleObj = handlers[ j ];

            // 参数mappedTypes存在或当前事件和handleObj中的当前事件同相
            if ( ( mappedTypes || origType === handleObj.origType ) &&
                // 并且参数handler不存在,或handler的ID与handleObj的ID同相
                ( !handler || handler.guid === handleObj.guid ) &&
                // 并且没有命名空间,或者是handleObj的命名空间子集
                ( !tmp || tmp.test( handleObj.namespace ) ) &&
                // 并且没有selector,或者selector与handleObj的selector同相,
                // 或者selector为"**"(表现恣意)并且handleObj的selector存在
                ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
                // 部全足满则删掉除当前事件象对
                handlers.splice( j, 1 );

                // 如果handleObj有selector
                if ( handleObj.selector ) {
                    handlers.delegateCount--;
                }
                // 如果特别事件remove存在,则用调special.remove
                // 应该和special.add对应,前目应该没什么用
                if ( special.remove ) {
                    special.remove.call( elem, handleObj );
                }
            }
        }

        // 如果存缓中来本存在事件理处象对,且当前没有事件理处象对
        // 证明部全在上面循环中删掉除了,就清掉除
        // 防止在潜的特别事件理处程序无限递归
        if ( origCount && !handlers.length ) {
            // 则试尝用special.teardown删除事件对handle的绑定
            if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
                // 不成功则应用removeEventListener删除绑定
                // 这里虽然还是这么写但实际上就是removeEventListener了
                jQuery.removeEvent( elem, type, elemData.handle );
            }

            // 删除存缓中对应事件理处函数表列
            delete events[ type ];
        }
    }

    // 如果存缓events经已空了,该象对没有任何事件绑定了
    if ( jQuery.isEmptyObject( events ) ) {
        // 在存缓中删除handle
        delete elemData.handle;

        // 清掉除events
        jQuery._removeData( elem, "events" );
    }
};

    

  • 实际上,要主是删除时要判断事件、理处函数、命名空间等是不是匹配,匹配才能删除。
  • 还有就是,如果该事件的理处函数队列空了就要需对该事件解绑定。
  • 如果改间时的事件表列都空了,那么就将主理处器,事件表列都删掉。

    

    然后剩下的解绑定函数都是由jQuery.fn.off扩展来的。

 

    

jQuery.fn.unbind

jQuery.fn.unbind: function( types, fn ) {
    return this.off( types, null, fn );
};
 

    

jQuery.fn.undelegate

jQuery.fn.undelegate = function( selector, types, fn ) {
    // ( namespace ) or ( selector, types [, fn] )
    return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
};
    每日一道理
盈盈月光,我掬一杯最清的;落落余辉,我拥一缕最暖的;灼灼红叶,我拾一片最热的;萋萋芳草,我摘一束最灿的;漫漫人生,我要采撷世间最重的———毅力。
 

    

jQuery.fn.trigger

jQuery.fn.trigger = function( type, data ) {
    return this.each(function() {
        jQuery.event.trigger( type, data, this );
    });
};

    jQuery.fn.trigger方法直接用调jQuery.event.trigger来模拟发消息。

    面下的jQuery.fn.triggerHandler也是通过jQuery.event.trigger来模拟发消息。

    

jQuery.fn.triggerHandler = function( type, data ) {
    var elem = this[0];
    if ( elem ) {
        return jQuery.event.trigger( type, data, elem, true );
    }
};
 

    

jQuery.event.trigger

jQuery.event.trigger = function( event, data, elem, onlyHandlers ) {

    var i, cur, tmp, bubbleType, ontype, handle, special,
        // 要需触发事件的部全元素队列
        eventPath = [ elem || document ],
        // 指定事件类型
        type = event.type || event,
        // 事件是不是有命名空间,有则分割成组数
        namespaces = event.namespace ? event.namespace.split(".") : [];

    cur = tmp = elem = elem || document;

    // 对于text和comment点节不停止事件理处
    if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
        return;
    }

    // 仅对focus/blur事件种变成focusin/out停止理处
    // 如果浏览器原生持支focusin/out,则保确当前不触发他们
    if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
        return;
    }

    // 如果type有命名空间
    if ( type.indexOf(".") >= 0 ) {
        // 从新组装事件
        namespaces = type.split(".");
        type = namespaces.shift();
        namespaces.sort();
    }
    // 看看是不是要需改成ontype式形
    ontype = type.indexOf(":") < 0 && "on" + type;

    // 看看这个是不是由jQuery.Event成生的例实,否则用jQuery.Event改革
    event = event[ jQuery.expando ] ?
        event :
        new jQuery.Event( type, typeof event === "object" && event );

    // 对event预理处
    event.isTrigger = true;
    event.namespace = namespaces.join(".");
    event.namespace_re = event.namespace ?
        new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
        null;

    // 除清数据,以从新应用
    event.result = undefined;
    // 如果事件没有触发元素,则用elem替代
    if ( !event.target ) {
        event.target = elem;
    }

    // 如果data为空,则传入理处函数的是event,否则由data和event构成
    data = data == null ?
        [ event ] :
        jQuery.makeArray( data, [ event ] );

    // 试尝通过特别事件停止理处,须要时候退出函数
    special = jQuery.event.special[ type ] || {};
    if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
        return;
    }

    // 如果要需泡冒,特别事件不要需止阻泡冒,且elem不是window象对
    if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {

        // 泡冒时是不是要需转成别的事件(用于事件模拟)
        bubbleType = special.delegateType || type;
        // 如果不是变形来的foucusin/out事件
        if ( !rfocusMorph.test( bubbleType + type ) ) {
            // 则定义当前元素师父点节
            cur = cur.parentNode;
        }
        // 遍历自身及部全父点节
        for ( ; cur; cur = cur.parentNode ) {
            // 推入要需触发事件的部全元素队列
            eventPath.push( cur );
            // 存一下循环中最后一个cur
            tmp = cur;
        }

        // 如果循环中最后一个cur是document,那么事件是要需最后触发到window象对上的
        // 将window象对推入元素队列
        if ( tmp === (elem.ownerDocument || document) ) {
            eventPath.push( tmp.defaultView || tmp.parentWindow || window );
        }
    }

    // 触发部全该事件对应元素的事件理处器
    i = 0;
    // 遍历部全元素,并保确事件不要需止阻泡冒
    while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {

        // 先肯定事件绑定类型是delegateType还是bindType
        event.type = i > 1 ?
            bubbleType :
            special.bindType || type;

        // 保确存缓中该元素对应事件中含包事件理处器,
        // 则出取主理处器(jQuery handle)来制控部全分事件理处器
        handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
        // 如果主理处器(jQuery handle)存在
        if ( handle ) {
            // 触发理处器
            handle.apply( cur, data );
        }

        // 出取原生事件理处器elem.ontype
        // 比如click事件就是elem.onclick
        handle = ontype && cur[ ontype ];
        // 如果原生事件理处器存在,看看需不要需止阻事件在浏览器上的默许动作
        if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
            event.preventDefault();
        }
    }
    // 保存事件类型,因为这时候事件可能变了
    event.type = type;

    // 如果不要需止阻默许动作,当即执行
    if ( !onlyHandlers && !event.isDefaultPrevented() ) {

        // 试尝通过特别事件触发默许动作
        if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
            !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {

            // 用调一个原生的DOM方法具有同相名称的名称作为事件的目标。
            // 例如对于事件click,elem.click()是触发该事件
            // 并保确不对window象对止阻默许事件
            if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {

                // 防止我们触发FOO()来触发其默许动作时,onFOO事件又触发了
                tmp = elem[ ontype ];

                // 清掉除该事件监听
                if ( tmp ) {
                    elem[ ontype ] = null;
                }

                // 当我们经已将事件向上起泡时,防止同相事件再次触发
                jQuery.event.triggered = type;
                // 触发事件
                elem[ type ]();
                // 成完除清标记
                jQuery.event.triggered = undefined;

                // 事件触发完了,可以把监听从新绑定归去
                if ( tmp ) {
                    elem[ ontype ] = tmp;
                }
            }
        }
    }

    return event.result;
};

    模拟触发为了让事件模型在各浏览器上表现分歧,花了不少的思心。

    反过来说,浏览器事件模型表现不分歧,至心折磨人……orz

    

 

文章结束给大家分享下程序员的一些笑话语录: 姿势要丰富,经常上百度!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值