Javascript 如何对与jQuery绑定的事件排序

Javascript 如何对与jQuery绑定的事件排序,javascript,jquery,events,Javascript,Jquery,Events,假设我有一个web应用程序,它的页面可能包含4个脚本块——我编写的脚本可能在其中一个块中找到,但我不知道哪个块由控制器处理 我将一些onclick事件绑定到一个按钮,但我发现它们有时会以我意想不到的顺序执行 是否有办法确保顺序,或者您过去是如何处理此问题的?如果顺序很重要,您可以创建自己的事件,并在其他回调触发这些事件时绑定回调以触发这些事件 $('#mydiv').click(function(e) { // maniplate #mydiv ... $('#mydiv').t

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

我将一些
onclick
事件绑定到一个按钮,但我发现它们有时会以我意想不到的顺序执行


是否有办法确保顺序,或者您过去是如何处理此问题的?

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

$('#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;
});

至少是这样。

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

Dowski的方法最好,您应该修改各种绑定回调以绑定到定制事件的有序序列,“第一个”回调绑定到“真实”事件。这样,无论以何种顺序绑定,序列都将以正确的方式执行

我能看到的唯一替代方法是您确实不想考虑的事情:如果您知道函数的绑定语法可能已经在您之前绑定过,请尝试解除所有这些函数的绑定,然后自己按照正确的顺序重新绑定它们。这只是自找麻烦,因为现在您有重复的代码


如果jQuery允许您简单地更改对象事件数据中绑定事件的顺序,但不编写一些代码来钩住jQuery核心(这似乎是不可能的),那就太酷了。允许这样做可能有一些我没有想到的含义,所以这可能是故意的疏忽。

您可以尝试以下方法:

/**
  * 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]);
    }   
}
/**
*确保事件处理程序始终是最后执行的
*@param owner使用任何其他事件处理程序$(选择器)创建jquery对象
*@param event事件描述符,如“click”
*@param handler事件处理程序将始终在末尾执行。
**/
函数bindAtTheEnd(所有者、事件、处理程序){
var aux=function(){owner.unbind(事件,处理程序);owner.bind(事件,处理程序);};
bindAtTheStart(所有者、事件、辅助、真实);
}
/**
*在所有其他事件处理程序的开头绑定事件处理程序。
*@param owner Jquery对象与任何其他事件处理程序$(选择器);
*@param event事件描述符,例如“click”;
*@param handler在开始时绑定的事件处理程序。
*@param one,如果函数只能执行一次。
**/
函数bindAtTheStart(所有者、事件、处理程序、一个){
var事件,指数;
var handlers=新数组();
取消绑定(事件、处理程序);
eventos=owner.data(“事件”)[event];

对于(index=0;indexDowski的方法是好的,如果您的所有回调始终存在,并且您对它们相互依赖感到满意

但是,如果希望回调彼此独立,可以利用冒泡并将后续事件作为委托附加到父元素。父元素上的处理程序将在元素上的处理程序之后触发,一直持续到文档。这非常好,因为可以使用
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插件强制将事件绑定到最后:


或者,如果您使用的是jQuery 1.5,您还可以对新的延迟对象做一些巧妙的操作。

我已经尝试了很久来概括这种过程,但在我的例子中,我只关心链中第一个事件侦听器的顺序

如果有任何用处,下面是我的jQuery插件,它绑定了一个总是在其他事件之前触发的事件侦听器:

**更新了jQuery更改(感谢Toskan)**

需要注意的事项:

  • 这还没有完全测试过
  • 它依赖于jQuery框架的内部结构,没有改变(仅使用1.5.2测试)
  • 它不一定会在以任何方式绑定的事件侦听器之前被触发,而不是作为源元素的属性或使用jQuery bind()和其他关联函数

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

element.data('events').action.reverse();
例如:

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

请注意,在jQuery中,从版本1.8开始,这必须以不同的方式实现

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

我们完全控制了处理器在jQuery宇宙中执行的顺序。Ricoo指出了这一点。他看起来并不像他的答案为他赢得了很多爱,但是这项技术非常方便。例如,考虑到在一个库小部件中的某个处理程序之前需要执行自己的处理程序的任何时候,或者你需要t。o有权有条件地取消对小部件处理程序的调用:

$("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();
});

下面是我的照片,涵盖了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;
            }
        });
    }
});

jQuery1.5引入了承诺,下面是我见过的控制执行顺序的最简单实现


在某些特殊情况下,当您无法更改click事件的绑定方式(事件绑定由其他人的代码生成)并且您可以更改HTML元素时,下面是一个可能的解决方案(警告:这不是绑定事件的推荐方式,其他开发人员可能会因此而谋杀您
$("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();
});
// 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;
            }
        });
    }
});
$.when( $('#myDiv').css('background-color', 'red') )
 .then( alert('hi!') )
 .then( myClickFunction( $('#myID') ) )
 .then( myThingToRunAfterClick() );
<span onclick="yourEventHandler(event)">Button</span>
$('form').submit(handle);
bindAtTheStart($('form'),'submit',handle);