it-swarm.com.de

So bestellen Sie Ereignisse, die mit jQuery verknüpft sind

Nehmen wir an, ich habe eine Web-App mit einer Seite, die möglicherweise 4 Skriptblöcke enthält. Das Skript, das ich schreibe, befindet sich möglicherweise in einem dieser Blöcke, aber ich weiß nicht, welcher davon von der Steuerung verarbeitet wird.

Ich binde einige onclick Ereignisse an eine Schaltfläche, aber ich stelle fest, dass sie manchmal in einer Reihenfolge ausgeführt werden, die ich nicht erwartet hatte.

Gibt es eine Möglichkeit, Ordnung zu gewährleisten, oder wie haben Sie dieses Problem in der Vergangenheit behandelt?

163
mkoryak

Ich hatte lange versucht, diese Art von Prozess zu verallgemeinern, aber in meinem Fall ging es mir nur um die Reihenfolge der ersten Event-Listener in der Kette.

Wenn es von Nutzen ist, hier ist mein jQuery-Plugin, das einen Ereignis-Listener bindet, der immer vor allen anderen ausgelöst wird:

** AKTUALISIERT inline mit 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);

Dinge zu beachten:

  • Dies wurde nicht vollständig getestet.
  • Es beruht darauf, dass sich die Interna des jQuery-Frameworks nicht ändern (nur mit 1.5.2 getestet).
  • Es wird nicht unbedingt vor Ereignis-Listenern ausgelöst, die auf andere Weise als als ein Attribut des Quellelements oder mithilfe von jQuery bind () und anderen zugeordneten Funktionen gebunden sind.
27
Ed .

Wenn die Reihenfolge wichtig ist, können Sie Ihre eigenen Ereignisse erstellen und Rückrufe an den Auslöser binden, 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;
});

Zumindest so etwas.

134
dowski

Dowskis Methode ist gut, wenn alle Ihre Rückrufe immer vorhanden sind und Sie damit zufrieden sind, dass sie voneinander abhängig sind.

Wenn Sie jedoch möchten, dass die Rückrufe unabhängig voneinander sind, können Sie das Bubbling nutzen und nachfolgende Ereignisse als Delegaten an übergeordnete Elemente anhängen. Die Handler für übergeordnete Elemente werden nach den Handlern für das Element ausgelöst und bis zum Dokument fortgesetzt. Dies ist sehr gut, da Sie mit event.stopPropagation(), event.preventDefault() usw. Handler überspringen und die Aktion abbrechen oder rückgängig machen können.

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

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

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

Wenn Ihnen das nicht gefällt, können Sie das bindLast-Plugin von Nick Leaches verwenden, um das letzte Binden eines Ereignisses zu erzwingen: https://github.com/nickyleach/jQuery.bindLast .

Wenn Sie jQuery 1.5 verwenden, können Sie möglicherweise auch mit dem neuen Deferred-Objekt eine clevere Aktion ausführen.

37
Daniel Lewis

Die Reihenfolge, in der die gebundenen Rückrufe aufgerufen werden, wird von den Ereignisdaten jedes jQuery-Objekts verwaltet. Es gibt keine (mir bekannten) Funktionen, mit denen Sie diese Daten direkt anzeigen und bearbeiten können. Sie können nur bind () und unfind () (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 Folge von benutzerdefinierten Ereignissen gebunden sind, wobei der "erste" Rückruf an das "echte" Ereignis gebunden ist. Auf diese Weise wird die Sequenz in der richtigen Reihenfolge ausgeführt, unabhängig davon, in welcher Reihenfolge sie gebunden sind.

Die einzige Alternative, die ich sehen kann, ist etwas, über das Sie wirklich, wirklich nicht nachdenken möchten: Wenn Sie wissen, dass die Bindungssyntax der Funktionen möglicherweise vor Ihnen gebunden wurde, versuchen Sie, alle diese Funktionen zu lösen und sie dann erneut zu binden in der richtigen Reihenfolge selbst. Das fragt nur nach Ärger, denn jetzt haben Sie Code dupliziert.

Es wäre cool, wenn Sie in jQuery einfach die Reihenfolge der gebundenen Ereignisse in den Ereignisdaten eines Objekts ändern könnten, ohne jedoch Code zu schreiben, der sich in den jQuery-Kern einfügt, was nicht möglich erscheint. Und es gibt wahrscheinlich Konsequenzen, wenn ich das zulasse, woran ich nicht gedacht habe. Vielleicht ist es eine absichtliche Unterlassung.

15
Adam Bellaire

Bitte beachten Sie, dass dies im jQuery-Universum ab Version 1.8 anders implementiert sein muss. Die folgende Release-Information stammt aus dem jQuery-Blog:

.data ("events"): jQuery speichert seine ereignisbezogenen Daten in einem Datenobjekt mit dem Namen (warte darauf) events für jedes Element. Dies ist eine interne Datenstruktur. In Version 1.8 wird diese aus dem Nutzerdaten-Namensraum entfernt, damit keine Konflikte mit gleichnamigen Elementen auftreten. Auf die Ereignisdaten von jQuery kann weiterhin über jQuery._data (Element, "events") zugegriffen werden.

Wir haben die vollständige Kontrolle über die Reihenfolge, in der die Handler im jQuery-Universum ausführen . Ricoo weist darauf hin. Es sieht nicht so aus, als hätte ihm seine Antwort viel Liebe eingebracht, aber diese Technik ist sehr praktisch. Betrachten Sie beispielsweise, wann immer Sie Ihren eigenen Handler ausführen müssen, bevor ein Handler in einem Bibliothekswidget ausgeführt wird, oder ob Sie die Möglichkeit haben müssen, den Aufruf des Handlers des Widgets unter bestimmten Bedingungen 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();
});
9
cage rattler

binden Sie einfach den Handler normal und führen Sie dann Folgendes aus:

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

so zum Beispiel:

$('#mydiv').data('events').click.reverse();
7
tomasbedrich
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;
}
7
robin

Sie können so etwas ausprobieren:

/**
  * 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]);
    }   
}
3
wildblue

Ich habe das gleiche Problem und dieses Thema gefunden. Die obigen Antworten können das Problem lösen, aber ich halte sie nicht für gute Pläne.

lasst uns über die reale Welt nachdenken.

wenn wir diese Antworten verwenden, müssen wir unseren Code ändern. Sie müssen Ihren Codestil ändern. etwas wie das:

original:

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

hacken:

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

denken Sie im Laufe der Zeit an Ihr Projekt. Der Code ist hässlich und schwer zu lesen! Ein weiterer Grund ist einfach, ist immer besser. Wenn Sie 10 bindAtTheStart haben, kann es keine Bugs geben. Wenn Sie 100 BindAtTheStart haben, sind Sie wirklich sicher, dass Sie sie in der richtigen Reihenfolge halten können?

also, wenn Sie die gleichen Ereignisse mehrfach binden müssen. Ich denke, der beste Weg ist, die Ladereihenfolge von js-Dateien oder js-Codes zu kontrollieren. jquery kann Ereignisdaten als Warteschlange behandeln. Die Reihenfolge ist First-In, First-Out. Sie müssen keinen Code ändern. Ladereihenfolge einfach ändern.

1
9nix00

Hier ist meine Aufnahme dazu, die verschiedene Versionen von jQuery abdeckt:

// 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;
            }
        });
    }
});
0
mightyiam

In einigen besonderen Fällen, in denen Sie die Bindung der Klickereignisse nicht ändern können (Ereignisbindungen werden aus den Codes anderer erstellt) und Sie das HTML-Element ändern können, gibt es eine mögliche Lösung (Warnung: this ist nicht der empfohlene Weg, Events zu binden, andere Entwickler könnten dich dafür ermorden):

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

Bei dieser Art der Bindung wird Ihr Event-Hander zuerst hinzugefügt und zuerst ausgeführt.

0
Linh Dam