假设我有一个 Web 应用程序,它的页面可能包含 4 个脚本块 - 我编写的脚本可能位于其中一个块中,但我不知道哪个由控制器处理。
我将一些onclick
事件绑定到一个按钮,但我发现它们有时会以我没想到的顺序执行。
有没有办法保证秩序,或者你过去是如何处理这个问题的?
假设我有一个 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;
});
至少是这样的。
如果您的所有回调始终存在并且您对它们相互依赖感到满意,则 Doowski 的方法是很好的。
但是,如果您希望回调彼此独立,则可以利用冒泡并将后续事件作为委托附加到父元素。父元素上的处理程序将在元素上的处理程序之后触发,一直持续到文档。这非常好,因为您可以使用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 对象做一些聪明的事情。
我一直试图概括这种过程,但就我而言,我只关心链中第一个事件侦听器的顺序。
如果它有任何用处,这里是我的 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 对象的事件数据管理。没有任何函数(据我所知)允许您直接查看和操作该数据,您只能使用 bind() 和 unbind()(或任何等效的辅助函数)。
Dowski 的方法是最好的,你应该修改各种绑定的回调以绑定到自定义事件的有序序列,“第一个”回调绑定到“真实”事件。这样,无论它们以什么顺序绑定,序列都会以正确的方式执行。
我能看到的唯一替代方法是您真的,真的不想考虑:如果您知道函数的绑定语法可能在您之前已经绑定,请尝试取消绑定所有这些函数,然后重新绑定它们自己按正确的顺序。那只是自找麻烦,因为现在您有重复的代码。
如果 jQuery 允许您简单地更改对象事件数据中绑定事件的顺序,而无需编写一些代码来挂钩 jQuery 核心,这似乎是不可能的,那将会很酷。并且可能有我没有想到的允许这样做的含义,所以这可能是有意的遗漏。
请注意,在 jQuery Universe 中,从 1.8 版开始,这必须以不同的方式实现。以下发行说明来自jQuery 博客:
.data(“events”):jQuery 将其与事件相关的数据存储在每个元素上名为(等待它)事件的数据对象中。这是一个内部数据结构,因此在 1.8 中,它将从用户数据名称空间中删除,因此它不会与同名项目发生冲突。jQuery 的事件数据仍然可以通过 jQuery._data(element, "events") 访问
我们确实可以完全控制处理程序在 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();
});