当前位置:  开发笔记 > 编程语言 > 正文

jQuery事件处理程序总是按照绑定的顺序执行 - 任何方式都可以解决这个问题?

如何解决《jQuery事件处理程序总是按照绑定的顺序执行-任何方式都可以解决这个问题?》经验,为你挑选了3个好方法。

jQuery事件处理程序总是以绑定的顺序执行,这可能很烦人.例如:

$('span').click(doStuff1);
$('span').click(doStuff2);

单击跨度将导致doStuff1()触发,然后doStuff2().

在我绑定doStuff2()时,我想 doStuff1()之前选择绑定它,但似乎没有任何简单的方法来执行此操作.

我想大多数人会说,只需编写如下代码:

$('span').click(function (){
    doStuff2();
    doStuff1();
});

但这只是一个简单的例子 - 在实践中,这样做并不总是方便.

在某些情况下,您要绑定事件,并且您绑定的对象已经有事件.在这种情况下,您可能只希望在任何其他现有事件之前触发新事件.

那么在jQuery中实现这一目标的最佳方法是什么?



1> Anurag..:

更新的答案

jQuery改变了1.8中存储事件的位置.现在你知道为什么乱搞内部API这是一个坏主意:)

用于访问DOM对象事件的新内部 API可通过全局jQuery对象获得,而不是绑定到每个实例,它将DOM元素作为第一个参数,并将一个键(我们的"事件")作为第二个参数.

jQuery._data(, "events");

所以这里是jQuery 1.8的修改代码.

// [name] is the name of the event "click", "mouseover", .. 
// same as you'd pass it to bind()
// [fn] is the handler function
$.fn.bindFirst = function(name, fn) {
    // bind as you normally would
    // don't want to miss out on any jQuery magic
    this.on(name, fn);

    // Thanks to a comment by @Martin, adding support for
    // namespaced events too.
    this.each(function() {
        var handlers = $._data(this, 'events')[name.split('.')[0]];
        // take out the handler we just inserted from the end
        var handler = handlers.pop();
        // move it at the beginning
        handlers.splice(0, 0, handler);
    });
};

这是一个操场.


原始答案

正如@Sean发现的那样,jQuery通过元素的data接口公开所有事件处理程序.具体element.data('events').使用它你总是可以编写一个简单的插件,你可以在特定的位置插入任何事件处理程序.

这是一个简单的插件,可以在列表的开头插入一个处理程序.您可以轻松扩展此项以在任何给定位置插入项目.这只是数组操作.但是由于我没有看到jQuery的源代码并且不想错过任何jQuery魔法,我通常bind首先使用处理程序,然后重新洗牌.

// [name] is the name of the event "click", "mouseover", .. 
// same as you'd pass it to bind()
// [fn] is the handler function
$.fn.bindFirst = function(name, fn) {
    // bind as you normally would
    // don't want to miss out on any jQuery magic
    this.bind(name, fn);

    // Thanks to a comment by @Martin, adding support for
    // namespaced events too.
    var handlers = this.data('events')[name.split('.')[0]];
    // take out the handler we just inserted from the end
    var handler = handlers.pop();
    // move it at the beginning
    handlers.splice(0, 0, handler);
};

因此,例如,对于此标记,它将起作用(例如此处):

..
$("#me").click(function() { alert("1"); }); $("#me").click(function() { alert("2"); }); $("#me").bindFirst('click', function() { alert("3"); }); $("#me").click(); // alerts - 3, then 1, then 2

但是,.data('events')据我所知,因为它不是其公共API的一部分,所以如果附加事件的基础表示从数组更改为其他内容,jQuery的更新可能会破坏您的代码.

免责声明:因为一切皆有可能:),这是你的解决方案,但我仍然会在重构现有代码方面犯错误,因为只要记住这些项目附加的顺序很快就会失控,因为你不断添加这些有序事件越来越多.


你是对的,如果.data()不是API的一部分,那么使用它是一种风险.但是,我喜欢你的建议,将它的使用封装在一个新的jQuery函数中.至少如果.data()在以后的版本中断,你只需要更新一个函数
要支持名称空间,请替换该行以获取处理程序:`var handlers = this.data('events')[name.split('.')[0]];`
从文档:调用jQuery.data(element)将所有元素的关联值检索为JavaScript对象.请注意,jQuery本身使用此方法存储**内部使用**的数据,例如**事件处理程序**,因此不要假设它只包含您自己的代码存储的数据.
好点..这样一来,改变将被隔离到一个功能.

2> RussellUrest..:

您可以执行事件的自定义命名空间.

$('span').bind('click.doStuff1',function(){doStuff1();});
$('span').bind('click.doStuff2',function(){doStuff2();});

然后,当您需要触发它们时,您可以选择订单.

$('span').trigger('click.doStuff1').trigger('click.doStuff2');

要么

$('span').trigger('click.doStuff2').trigger('click.doStuff1');

此外,只需触发click按钮,它们按照绑定的顺序触发......所以你仍然可以

$('span').trigger('click'); 



3> Sean Vieira..:

一个非常好的问题......我很感兴趣所以我做了一点挖掘; 对于那些感兴趣的人,这里是我去的地方,以及我想出的.

查看jQuery 1.4.2的源代码,我在第2361行和第2392行之间看到了这个块:

jQuery.each(["bind", "one"], function( i, name ) {
    jQuery.fn[ name ] = function( type, data, fn ) {
        // Handle object literals
        if ( typeof type === "object" ) {
            for ( var key in type ) {
                this[ name ](key, data, type[key], fn);
            }
            return this;
        }

        if ( jQuery.isFunction( data ) ) {
            fn = data;
            data = undefined;
        }

        var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
            jQuery( this ).unbind( event, handler );
            return fn.apply( this, arguments );
        }) : fn;

        if ( type === "unload" && name !== "one" ) {
            this.one( type, data, fn );

        } else {
            for ( var i = 0, l = this.length; i < l; i++ ) {
                jQuery.event.add( this[i], type, handler, data );
            }
        }

        return this;
    };
});

这里有很多有趣的东西,但我们感兴趣的部分是在2384和2388行之间:

else {
    for ( var i = 0, l = this.length; i < l; i++ ) {
        jQuery.event.add( this[i], type, handler, data );
    }
}

每当我们打电话bind()或者one()我们实际打电话给jQuery.event.add()...时,让我们来看看(1557到1672行,如果你有兴趣的话)

add: function( elem, types, handler, data ) {
// ... snip ...
        var handleObjIn, handleObj;

        if ( handler.handler ) {
            handleObjIn = handler;
            handler = handleObjIn.handler;
        }

// ... snip ...

        // Init the element's event structure
        var elemData = jQuery.data( elem );

// ... snip ...

        var events = elemData.events = elemData.events || {},
            eventHandle = elemData.handle, eventHandle;

        if ( !eventHandle ) {
            elemData.handle = eventHandle = function() {
                // Handle the second event of a trigger and when
                // an event is called after a page has unloaded
                return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
                    jQuery.event.handle.apply( eventHandle.elem, arguments ) :
                    undefined;
            };
        }

// ... snip ...

        // Handle multiple events separated by a space
        // jQuery(...).bind("mouseover mouseout", fn);
        types = types.split(" ");

        var type, i = 0, namespaces;

        while ( (type = types[ i++ ]) ) {
            handleObj = handleObjIn ?
                jQuery.extend({}, handleObjIn) :
                { handler: handler, data: data };

            // Namespaced event handlers
                    ^
                    |
      // There is is! Even marked with a nice handy comment so you couldn't miss it 
      // (Unless of course you are not looking for it ... as I wasn't)

            if ( type.indexOf(".") > -1 ) {
                namespaces = type.split(".");
                type = namespaces.shift();
                handleObj.namespace = namespaces.slice(0).sort().join(".");

            } else {
                namespaces = [];
                handleObj.namespace = "";
            }

            handleObj.type = type;
            handleObj.guid = handler.guid;

            // Get the current list of functions bound to this event
            var handlers = events[ type ],
                special = jQuery.event.special[ type ] || {};

            // Init the event handler queue
            if ( !handlers ) {
                handlers = events[ type ] = [];

                   // ... snip ...

            }

                  // ... snip ...

            // Add the function to the element's handler list
            handlers.push( handleObj );

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

     // ... snip ...
    }

在这一点上,我意识到理解这将花费超过30分钟......所以我搜索了Stackoverflow

jquery get a list of all event handlers bound to an element

并找到了迭代绑定事件的答案:

//log them to the console (firebug, ie8)
console.dir( $('#someElementId').data('events') );

//or iterate them
jQuery.each($('#someElementId').data('events'), function(i, event){

    jQuery.each(event, function(i, handler){

        console.log( handler.toString() );

    });

});

测试在Firefox中我看到每个元素eventsdata属性中的对象都有一个[some_event_name]属性(click在我们的例子中)附加了一个handler对象数组,每个对象都有一个guid,一个命名空间,一个类型和一个处理程序."那么",我认为,"理论上我们应该能够以相同的方式添加对象[element].data.events.[some_event_name].push([our_handler_object);......"

然后我去写完了我的发现......并找到很多更好的答案张贴RusselUresti ......,介绍我到新的东西,我不知道的jQuery(即使我盯着它的脸右侧.)

这证明Stackoverflow是互联网上最好的问答网站,至少在我的拙见中.

所以我发布这个是为了后人的缘故......并将其标记为社区维基,因为RussellUresti已经很好地回答了这个问题.

推荐阅读
kikokikolove
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有