Answers:
我花了很长时间来概括这种过程,但就我而言,我只关心链中第一个事件侦听器的顺序。
如果有什么用,这里是我的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);
注意事项:
this.data("events")
,看到这里stackoverflow.com/questions/12214654/...
如果顺序很重要,则可以创建自己的事件并绑定回调以在其他回调触发这些事件时触发。
$('#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;
});
至少是这样的。
如果所有回调都将始终存在并且您对它们相互依赖感到满意,则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对象进行巧妙的处理。
.delegate
已不再使用,您现在应该使用.on
,但我不知道您如何使用on
绑定回调的调用顺序由每个jQuery对象的事件数据管理。没有任何函数(我知道)允许您直接查看和操作该数据,只能使用bind()和unbind()(或任何等效的帮助器函数)。
Dowski的方法是最好的,您应该修改各种绑定的回调以绑定到有序的自定义事件序列,并将“第一个”回调绑定到“真实”事件。这样,无论它们以什么顺序绑定,序列都将以正确的方式执行。
我可以看到的唯一选择是您确实非常不想考虑的事情:如果您知道这些函数的绑定语法可能已经在您之前绑定了,请尝试取消所有这些函数的绑定,然后重新进行绑定自己以正确的顺序。那只是自找麻烦,因为现在您已经重复了代码。
如果jQuery允许您简单地更改对象事件数据中绑定事件的顺序,但又无需编写一些似乎无法挂接到jQuery核心的代码,那将很酷。可能存在允许我没有想到的含义,因此这可能是故意遗漏的。
请注意,在jQuery Universe中,此版本必须从1.8版开始以不同的方式实现。以下发行说明来自jQuery博客:
.data(“ events”):jQuery将其与事件相关的数据存储在每个元素上名为(等待它)事件的数据对象中。这是一个内部数据结构,因此在1.8中,它将从用户数据名称空间中删除,因此不会与同名项目冲突。仍然可以通过jQuery._data(element,“ events”)访问jQuery的事件数据。
我们确实完全控制了处理程序在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();
});
只需正常绑定处理程序,然后运行:
element.data('events').action.reverse();
因此,例如:
$('#mydiv').data('events').click.reverse();
$._data(element, 'events')[name].reverse()
有效
您可以尝试如下操作:
/**
* 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]);
}
}
我有同样的问题,并找到了这个话题。上述答案可以解决这些问题,但我认为它们不是好的计划。
让我们考虑一下现实世界。
如果使用这些答案,则必须更改代码。您必须更改代码样式。像这样的东西:
原版的:
$('form').submit(handle);
骇客:
bindAtTheStart($('form'),'submit',handle);
随着时间的流逝,考虑一下您的项目。代码丑陋且难以阅读!原因简单总是更好。如果您有10 bindAtTheStart,则可能没有错误。如果您有100 bindAtTheStart,您真的确定可以使其顺序正确吗?
因此,如果必须将多个相同的事件绑定。我认为最好的方法是控制js文件或js代码的加载顺序。jQuery可以将事件数据作为队列处理。顺序是先进先出。您不需要更改任何代码。只需更改加载顺序即可。
这是我的镜头,涵盖了不同版本的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;
}
});
}
});
JQuery 1.5引入了promises,这是我见过的最简单的实现,用于控制执行顺序。完整文档位于http://api.jquery.com/jquery.when/
$.when( $('#myDiv').css('background-color', 'red') )
.then( alert('hi!') )
.then( myClickFunction( $('#myID') ) )
.then( myThingToRunAfterClick() );