如何订购与jQuery绑定的事件

时间:2008-11-14 14:39:20

标签: javascript jquery events

假设我有一个Web应用程序,其页面可能包含4个脚本块 - 我写的脚本可能在其中一个块中找到,但我不知道哪个是由控制器处理的。

我将一些onclick事件绑定到一个按钮,但我发现它们有时按照我没想到的顺序执行。

有没有办法确保订单,或者您过去如何处理此问题?

12 个答案:

答案 0 :(得分:134)

如果顺序很重要,您可以创建自己的事件并绑定回调,以便在其他回调触发这些事件时触发。

$('#mydiv').click(function(e) {
    // maniplate #mydiv ...
    $('#mydiv').trigger('mydiv-manipulated');
});

$('#mydiv').bind('mydiv-manipulated', function(e) {
    // do more stuff now that #mydiv has been manipulated
    return;
});

至少这样的事情。

答案 1 :(得分:37)

Dowski的方法很好,如果你的所有回调总是存在,并且你对他们彼此依赖感到满意。

如果您希望回调彼此独立,您可以利用冒泡并将后续事件作为委托附加到父元素。父元素上的处理程序将在元素上的处理程序之后触发,一直持续到文档。这非常好,因为您可以使用event.stopPropagation()event.preventDefault()等来跳过处理程序并取消或取消取消操作。

$( '#mybutton' ).click( function(e) { 
    // Do stuff first
} );

$( '#mybutton' ).click( function(e) { 
    // Do other stuff first
} );

$( document ).delegate( '#mybutton', 'click', function(e) {
    // Do stuff last
} );

或者,如果您不喜欢这样,可以使用Nick Leaches bindLast插件强制事件最后绑定:https://github.com/nickyleach/jQuery.bindLast

或者,如果您使用的是jQuery 1.5,您还可以使用新的Deferred对象做一些聪明的事情。

答案 2 :(得分:27)

我多年来一直试图推广这种过程,但在我的情况下,我只关心链中第一个事件监听器的顺序。

如果它有用,这里是我的jQuery插件,它绑定一个总是在任何其他人之前触发的事件监听器:

** 更新内联jQuery更改(感谢Toskan) **

(function($) {
    $.fn.bindFirst = function(/*String*/ eventType, /*[Object])*/ eventData, /*Function*/ handler) {
        var indexOfDot = eventType.indexOf(".");
        var eventNameSpace = indexOfDot > 0 ? eventType.substring(indexOfDot) : "";

        eventType = indexOfDot > 0 ? eventType.substring(0, indexOfDot) : eventType;
        handler = handler == undefined ? eventData : handler;
        eventData = typeof eventData == "function" ? {} : eventData;

        return this.each(function() {
            var $this = $(this);
            var currentAttrListener = this["on" + eventType];

            if (currentAttrListener) {
                $this.bind(eventType, function(e) {
                    return currentAttrListener(e.originalEvent); 
                });

                this["on" + eventType] = null;
            }

            $this.bind(eventType + eventNameSpace, eventData, handler);

            var allEvents = $this.data("events") || $._data($this[0], "events");
            var typeEvents = allEvents[eventType];
            var newEvent = typeEvents.pop();
            typeEvents.unshift(newEvent);
        });
    };
})(jQuery);

需要注意的事项:

  • 尚未经过全面测试。
  • 它依赖于jQuery框架的内部不变(仅用1.5.2测试)。
  • 除了作为源元素的属性或使用jQuery bind()和其他相关函数之外的任何方式绑定的事件侦听器之外,它不一定会被触发。

答案 3 :(得分:15)

调用绑定回调的顺序由每个jQuery对象的事件数据管理。没有任何函数(我知道)允许您直接查看和操作该数据,您只能使用bind()和unbind()(或任何等效的辅助函数)。

Dowski的方法是最好的,您应该修改各种绑定回调以绑定到自定义事件的有序序列,并将“first”回调绑定到“real”事件。这样,无论它们绑定的顺序如何,序列都将以正确的方式执行。

我能看到的唯一选择是你真的,真的不想考虑:如果你知道函数的绑定语法可能已经绑定在你之前,试图解除所有这些函数的绑定然后重新 - 自己以正确的顺序绑定它们。这只是问麻烦,因为现在你有重复的代码。

如果jQuery允许你简单地改变对象事件数据中绑定事件的顺序,但是没有编写一些代码来挂钩到似乎不可能的jQuery核心,那将会很酷。并且可能存在允许我没有想到的含义,所以也许这是故意遗漏。

答案 4 :(得分:8)

请注意,在jQuery Universe中,从版本1.8开始,这必须以不同的方式实现。以下发行说明来自the jQuery blog:

  

.data(“events”):jQuery将其事件相关数据存储在数据对象中   在每个元素上命名(等待)事件。这是一个内部数据   因此在1.8中这将从用户数据名称空间中删除   所以它不会与同名的项目冲突。 jQuery的事件数据   仍然可以通过jQuery._data(元素,“事件”)

访问

我们可以完全控制处理程序在jQuery Universe 中执行的顺序。 Ricoo在上面指出了这一点。看起来他的回答并没有给他带来很多爱,但这种技巧非常方便。例如,考虑到您需要在库窗口小部件中的某个处理程序之前执行自己的处理程序,或者您需要有条件地取消对窗口小部件处理程序的调用的权力:

$("button").click(function(e){
    if(bSomeConditional)
       e.stopImmediatePropagation();//Don't execute the widget's handler
}).each(function () {
    var aClickListeners = $._data(this, "events").click;
    aClickListeners.reverse();
});

答案 5 :(得分:7)

function bindFirst(owner, event, handler) {
    owner.unbind(event, handler);
    owner.bind(event, handler);

    var events = owner.data('events')[event];
    events.unshift(events.pop());

    owner.data('events')[event] = events;
}

答案 6 :(得分:6)

正常绑定处理程序然后运行:

element.data('events').action.reverse();

所以例如:

$('#mydiv').data('events').click.reverse();

答案 7 :(得分:3)

您可以尝试这样的事情:

/**
  * Guarantee that a event handler allways be the last to execute
  * @param owner The jquery object with any others events handlers $(selector)
  * @param event The event descriptor like 'click'
  * @param handler The event handler to be executed allways at the end.
**/
function bindAtTheEnd(owner,event,handler){
    var aux=function(){owner.unbind(event,handler);owner.bind(event,handler);};
    bindAtTheStart(owner,event,aux,true);

}
/**
  * Bind a event handler at the start of all others events handlers.
  * @param owner Jquery object with any others events handlers $(selector);
  * @param event The event descriptor for example 'click';
  * @param handler The event handler to bind at the start.
  * @param one If the function only be executed once.
**/
function bindAtTheStart(owner,event,handler,one){
    var eventos,index;
    var handlers=new Array();
    owner.unbind(event,handler);
    eventos=owner.data("events")[event];
    for(index=0;index<eventos.length;index+=1){
        handlers[index]=eventos[index];
    }
    owner.unbind(event);
    if(one){
        owner.one(event,handler);
    }
    else{
        owner.bind(event,handler);
    }
    for(index=0;index<handlers.length;index+=1){
        owner.bind(event,ownerhandlers[index]);
    }   
}

答案 8 :(得分:1)

我有同样的问题并找到了这个主题。上述答案可以解决这些问题,但我不认为它们是好的计划。

让我们想想现实世界。

如果我们使用这些答案,我们必须更改我们的代码。你必须改变你的代码风格。像这样的东西:

原:

$('form').submit(handle);

劈:

bindAtTheStart($('form'),'submit',handle);

随着时间的推移,想想你的项目。代码难看,难以阅读! anthoer原因很简单,总是更好。如果你有10个bindAtTheStart,它可能没有错误。如果你有100个bindAtTheStart,你真的确定你能保持正确的秩序吗?

所以如果你必须多次绑定相同的事件。我认为最好的方法是控制js-file或js-code加载顺序。 jquery可以将事件数据作为队列处理。订单是先进先出。你不需要改变任何代码。只需更改加载顺序。

答案 9 :(得分:0)

这是我对此的镜头,涵盖不同版本的jQuery:

// Binds a jQuery event to elements at the start of the event chain for that type.
jQuery.extend({
    _bindEventHandlerAtStart: function ($elements, eventType, handler) {
        var _data;

        $elements.bind(eventType, handler);
        // This bound the event, naturally, at the end of the event chain. We
        // need it at the start.

        if (typeof jQuery._data === 'function') {
            // Since jQuery 1.8.1, it seems, that the events object isn't
            // available through the public API `.data` method.
            // Using `$._data, where it exists, seems to work.
            _data = true;
        }

        $elements.each(function (index, element) {
            var events;

            if (_data) {
                events = jQuery._data(element, 'events')[eventType];
            } else {
                events = jQuery(element).data('events')[eventType];
            }

            events.unshift(events.pop());

            if (_data) {
                jQuery._data(element, 'events')[eventType] = events;
            } else {
                jQuery(element).data('events')[eventType] = events;
            }
        });
    }
});

答案 10 :(得分:0)

在某些特殊情况下,当您无法更改点击事件的绑定方式(事件绑定来自其他人的代码),并且您可以更改HTML元素时,这是一个可能的解决方案(警告< / strong>:这是绑定事件的推荐方式,其他开发人员可能会因此而谋杀你):

<span onclick="yourEventHandler(event)">Button</span>

通过这种绑定方式,您的事件处理程序将首先添加,因此将首先执行。

答案 11 :(得分:-2)

JQuery 1.5引入了promises,这是我用来控制执行顺序的最简单的实现。 http://api.jquery.com/jquery.when/

的完整文档
$.when( $('#myDiv').css('background-color', 'red') )
 .then( alert('hi!') )
 .then( myClickFunction( $('#myID') ) )
 .then( myThingToRunAfterClick() );