javascript - Wie man mit jQuery gebundene Ereignisse sortiert
Angenommen, ich habe eine Webanwendung, die eine Seite enthält, die möglicherweise 4 Skriptblöcke hat - das Skript, das ich schreibe, kann in einem dieser Blöcke gefunden werden, aber ich weiß nicht, welcher es ist, das wird vom Controller gehandhabt.
Ich binde einige onclick-Ereignisse an einen Button, aber ich stelle fest, dass sie manchmal in einer Reihenfolge ausgeführt werden, die ich nicht erwartet habe.
Gibt es eine Möglichkeit, Ordnung zu gewährleisten, oder wie sind Sie in der Vergangenheit mit diesem Problem umgegangen?
Antwort #1
Wenn die Reihenfolge wichtig ist, können Sie Ihre eigenen Ereignisse erstellen und Rückrufe binden, die ausgelöst werden, wenn diese Ereignisse von anderen Rückrufen ausgelöst werden.
$('#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;
});
So etwas zumindest.
Antwort #2
Dowskis Methode ist gut, wenn alle Ihre Rückrufe immer vorhanden sein werden und Sie damit einverstanden sind, dass sie voneinander abhängig sind.
Wenn Sie möchten, dass die Rückrufaktionen unabhängig voneinander sind, könnten Sie jedoch die Vorteile des Aufblähens nutzen und nachfolgende Ereignisse als Delegaten an übergeordnete Elemente anhängen. Die Handler auf einem übergeordneten Element werden nach den Handlern des Elements ausgelöst, und dies setzt sich bis zum Dokument fort. Dies ist ziemlich gut, da Sie event.stopPropagation(), event.preventDefault() usw. verwenden können, um Handler zu überspringen und die Aktion abzubrechen oder wieder zuzulassen.
$( '#mybutton' ).click( function(e) {
// Do stuff first
} );
$( '#mybutton' ).click( function(e) {
// Do other stuff first
} );
$( document ).delegate( '#mybutton', 'click', function(e) {
// Do stuff last
} );
Oder, wenn Ihnen das nicht gefällt, könnten Sie Nick Leaches bindLast-Plugin verwenden, um ein Ereignis zu erzwingen, zuletzt gebunden zu werden: https://github.com/nickyleach/jQuery.bindLast.
Oder, wenn Sie jQuery 1.5 verwenden, könnten Sie möglicherweise auch etwas Cleveres mit dem neuen Deferred-Objekt machen.
Antwort #3
Ich hatte schon seit Ewigkeiten versucht, diesen Art von Prozess zu verallgemeinern, aber in meinem Fall ging es mir nur um die Reihenfolge des ersten Ereignislisteners in der Kette.
Falls es von Nutzen ist, hier ist mein jQuery-Plugin, das einen Ereignis-Listener bindet, der immer vor allen anderen ausgelöst wird:
** AKTUALISIERT entsprechend den jQuery-Änderungen (danke 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);
Zu beachten:
- Dies wurde nicht vollständig getestet.
- Es hängt davon ab, dass sich die internen Strukturen des jQuery-Frameworks nicht ändern (nur mit Version 1.5.2 getestet).
- Es wird nicht notwendigerweise vor Ereignis-Listenern ausgelöst, die auf andere Weise als als Attribut des Quell-Elements oder unter Verwendung von jQuery bind() und anderen zugehörigen Funktionen gebunden sind.
Antwort #4
Die Reihenfolge, in der die gebundenen Rückruf-Funktionen aufgerufen werden, wird durch die Ereignisdaten jedes jQuery-Objekts verwaltet. Es gibt keine Funktionen (soweit ich weiß), die es Ihnen erlauben, diese Daten direkt einzusehen und zu manipulieren, Sie können nur bind() und unbind() (oder eine der entsprechenden Hilfsfunktionen) verwenden.
Dowskis Methode ist am besten, Sie sollten die verschiedenen gebundenen Rückrufe so ändern, dass sie an eine geordnete Sequenz von benutzerdefinierten Ereignissen gebunden sind, wobei der „erste“ Rückruf an das „echte“ Ereignis gebunden ist. Auf diese Weise wird die Sequenz unabhängig von der Reihenfolge ihrer Bindung korrekt ausgeführt.
Die einzige Alternative, die ich sehe, ist etwas, das Sie wirklich, wirklich nicht in Betracht ziehen möchten: Wenn Sie wissen, dass die Bindesyntax der Funktionen möglicherweise bereits vor Ihnen gebunden wurde, versuchen Sie, alle diese Funktionen zu entbinden und sie dann selbst in der richtigen Reihenfolge erneut zu binden. Das ist nur eine Einladung zu Problemen, weil Sie jetzt doppelten Code haben.
Es wäre cool, wenn jQuery es einem einfach erlauben würde, die Reihenfolge der gebundenen Ereignisse in den Ereignisdaten eines Objekts zu ändern, aber ohne etwas Code zu schreiben, um in den jQuery-Kern einzugreifen, scheint das nicht möglich zu sein. Und es gibt wahrscheinlich Auswirkungen, dieses zu erlauben, an die ich noch nicht gedacht habe, also ist es vielleicht eine absichtliche Auslassung.
Antwort #5
Bitte beachten Sie, dass dies im jQuery-Universum ab Version 1.8 anders implementiert werden muss. Die folgende Versionshinweis stammt aus dem jQuery-Blog:
.data("events"): jQuery speichert seine ereignisbezogenen Daten in einem Datenobjekt namens (wartet darauf) events auf jedem Element. Dies ist eine interne Datenstruktur, daher wird sie in Version 1.8 aus dem Benutzerdaten-Namensraum entfernt, sodass sie nicht mit Elementen desselben Namens in Konflikt gerät. Auf die Ereignisdaten von jQuery kann dennoch über jQuery._data(element, "events") zugegriffen werden.
Wir haben tatsächlich die vollständige Kontrolle über die Reihenfolge, in der die Handler im jQuery-Universum ausgeführt werden. Ricoo weist oben darauf hin. Es sieht nicht so aus, als hätte seine Antwort viel Anerkennung bekommen, aber diese Technik ist sehr nützlich. Betrachten wir zum Beispiel jedes Mal, wenn Sie Ihren eigenen Handler vor einem Handler in einem Bibliotheks-Widget ausführen müssen, oder wenn Sie die Möglichkeit haben müssen, den Aufruf des Handlers des Widgets bedingt abzubrechen:
$("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();
});
