Benutzerdefinierte Ereignisse in jQuery?

180

Ich suche nach Anregungen, wie man benutzerdefiniertes Eventhandling in jquery am besten implementiert. Ich weiß, wie man Ereignisse aus den dom-Elementen wie 'click' usw. verbindet, aber ich baue eine winzige Javascript-Bibliothek / ein Plugin, um einige Vorschaufunktionen zu handhaben.

Ich habe ein Skript ausgeführt, um Text in einem dom-Element anhand einer Reihe von Regeln und Daten- / Benutzereingaben zu aktualisieren, aber jetzt muss derselbe Text in anderen Elementen angezeigt werden, von denen dieses Skript möglicherweise nichts wissen kann. Was ich brauche, ist ein gutes Muster, um dieses Skript zu beobachten, das den benötigten Text erzeugt.

Wie mache ich das? Habe ich einige in jquery integrierte Funktionen übersehen, um Benutzerereignisse auszulösen / zu verarbeiten, oder benötige ich dafür ein jquery-Plugin? Was ist Ihrer Meinung nach der beste Weg / das beste Plugin, um damit umzugehen?

Per Hornshøj-Schierbeck
quelle
3
Es gibt ein anderes Framework, knockoutjs.com , das mein altes Problem tatsächlich lösen kann, wenn jemand, der sich diese Frage ansieht, es braucht.
Per Hornshøj-Schierbeck
2
Angesichts der Tatsache, dass ich durch diese Frage immer wieder einen guten Ruf bekomme, müssen die Leute sie immer noch lesen. Ich möchte nur meine aktuelle Auswahl des clientseitigen MVC-Frameworks aktualisieren
Per Hornshøj-Schierbeck
Obwohl es sich völlig von Angular V1 unterscheidet, ist Angular2 ( Angular.io ) so vielversprechend und wird meine erste Wahl sein, sobald Beta / RC beendet ist.
Per Hornshøj-Schierbeck

Antworten:

105

Schau dir das an:

(Nachdruck von der abgelaufenen Blog-Seite http://jamiethompson.co.uk/web/2008/06/17/publish-subscribe-with-jquery/ basierend auf der archivierten Version unter http://web.archive.org/web /20130120010146/http://jamiethompson.co.uk/web/2008/06/17/publish-subscribe-with-jquery/ )


Veröffentlichen / Abonnieren mit jQuery

17. Juni 2008

Um eine jQuery-Benutzeroberfläche zu schreiben, die in die Offline-Funktionalität von Google Gears integriert ist, habe ich mit Code gespielt, um mithilfe von jQuery den Netzwerkverbindungsstatus abzufragen.

Das Netzwerkerkennungsobjekt

Die Grundvoraussetzung ist sehr einfach. Wir erstellen eine Instanz eines Netzwerkerkennungsobjekts, das in regelmäßigen Abständen eine URL abfragt. Sollten diese HTTP-Anforderungen fehlschlagen, können wir davon ausgehen, dass die Netzwerkverbindung unterbrochen wurde oder der Server zum gegenwärtigen Zeitpunkt einfach nicht erreichbar ist.

$.networkDetection = function(url,interval){
    var url = url;
    var interval = interval;
    online = false;
    this.StartPolling = function(){
        this.StopPolling();
        this.timer = setInterval(poll, interval);
    };
    this.StopPolling = function(){
        clearInterval(this.timer);
    };
    this.setPollInterval= function(i) {
        interval = i;
    };
    this.getOnlineStatus = function(){
        return online;
    };
    function poll() {
        $.ajax({
            type: "POST",
            url: url,
            dataType: "text",
            error: function(){
                online = false;
                $(document).trigger('status.networkDetection',[false]);
            },
            success: function(){
                online = true;
                $(document).trigger('status.networkDetection',[true]);
            }
        });
    };
};

Sie können die Demo hier ansehen. Stellen Sie Ihren Browser so ein, dass er offline arbeitet und sehen Sie, was passiert. Nein, es ist nicht sehr aufregend.

Auslösen und binden

Was jedoch aufregend ist (oder zumindest was mich begeistert), ist die Methode, mit der der Status über die Anwendung weitergeleitet wird. Ich bin auf eine weitgehend unbekannte Methode zur Implementierung eines Pub / Sub-Systems mit den Trigger- und Bind-Methoden von jQuery gestoßen.

Der Demo-Code ist stumpfer als nötig. Das Netzwerkerkennungsobjekt veröffentlicht Statusereignisse in dem Dokument, das sie aktiv abhört, und veröffentlicht wiederum Benachrichtigungsereignisse für alle Abonnenten (dazu später mehr). Der Grund dafür ist, dass in einer realen Anwendung wahrscheinlich mehr Logik gesteuert wird, wann und wie die Benachrichtigungsereignisse veröffentlicht werden.

$(document).bind("status.networkDetection", function(e, status){
    // subscribers can be namespaced with multiple classes
    subscribers = $('.subscriber.networkDetection');
    // publish notify.networkDetection even to subscribers
    subscribers.trigger("notify.networkDetection", [status])
    /*
    other logic based on network connectivity could go here
    use google gears offline storage etc
    maybe trigger some other events
    */
});

Aufgrund des DOM-zentrierten Ansatzes von jQuery werden Ereignisse in (ausgelöst auf) DOM-Elementen veröffentlicht. Dies kann das Fenster- oder Dokumentobjekt für allgemeine Ereignisse sein oder Sie können ein jQuery-Objekt mithilfe eines Selektors generieren. Der Ansatz, den ich mit der Demo gewählt habe, besteht darin, einen fast namenlosen Ansatz für die Definition von Abonnenten zu erstellen.

DOM-Elemente, die Abonnenten sein sollen, werden einfach mit "Abonnent" und "Netzwerkerkennung" klassifiziert. Wir können dann Ereignisse nur für diese Elemente veröffentlichen (von denen es nur eines in der Demo gibt), indem wir ein Benachrichtigungsereignis für auslösen$(“.subscriber.networkDetection”)

Dem #notifierDiv, das Teil der .subscriber.networkDetectionGruppe von Abonnenten ist, ist dann eine anonyme Funktion zugeordnet, die effektiv als Zuhörer fungiert.

$('#notifier').bind("notify.networkDetection",function(e, online){
    // the following simply demonstrates
    notifier = $(this);
    if(online){
        if (!notifier.hasClass("online")){
            $(this)
                .addClass("online")
                .removeClass("offline")
                .text("ONLINE");
        }
    }else{
        if (!notifier.hasClass("offline")){
            $(this)
                .addClass("offline")
                .removeClass("online")
                .text("OFFLINE");
        }
    };
});

Hier bitteschön. Es ist alles ziemlich ausführlich und mein Beispiel ist überhaupt nicht aufregend. Es zeigt auch nichts Interessantes, was Sie mit diesen Methoden tun könnten, aber wenn jemand überhaupt daran interessiert ist, die Quelle zu durchsuchen, fühlen Sie sich frei. Der gesamte Code befindet sich im Kopf der Demoseite

Vitor Silva
quelle
Genau das, wonach ich gesucht habe. Er hat wahrscheinlich Recht, dass es der richtige Weg ist, aber ich kann mir nicht helfen zu denken, dass jquery entweder direkten Support oder ein Plugin benötigt, um es eleganter zu handhaben. Ich werde ein bisschen warten und sehen, ob es andere
Per Hornshøj-Schierbeck
6
Ist das ein Zitat von irgendwoher? Wenn ja, geben Sie bitte Ihre Quelle an und geben Sie einen Link an, falls dieser noch vorhanden ist.
Aryeh Leib Taurog
1
Ja, es ist ein Zitat. Im Bearbeitungsverlauf befindet sich dieser Link . Derzeit ist er nicht zugänglich. Die archivierte Seite ist hier .
Stano
1
Ben Alman hat ein winziges jQuery Pub Sub Plugin geschrieben . Es ist sehr elegant.
Barney
127

Der in der akzeptierten Antwort angegebene Link zeigt eine gute Möglichkeit, das Pub / Sub-System mit jQuery zu implementieren , aber ich fand den Code etwas schwierig zu lesen. Hier ist meine vereinfachte Version des Codes:

http://jsfiddle.net/tFw89/5/

$(document).on('testEvent', function(e, eventInfo) { 
  subscribers = $('.subscribers-testEvent');
  subscribers.trigger('testEventHandler', [eventInfo]);
});

$('#myButton').on('click', function() {
  $(document).trigger('testEvent', [1011]);
});

$('#notifier1').on('testEventHandler', function(e, eventInfo) { 
  alert('(notifier1)The value of eventInfo is: ' + eventInfo);
});

$('#notifier2').on('testEventHandler', function(e, eventInfo) { 
  alert('(notifier2)The value of eventInfo is: ' + eventInfo);
});
Manuel Navarro
quelle
10
Diese Antwort gefällt mir sehr gut. Jeder kann die Dokumente unter trigger () bind () (und on ()) lesen. Diese Antwort bietet jedoch ein konkretes Beispiel für die Erstellung eines Ereignisbusses (Pub / Subsystem) mit jquery.
lostdorje
1
Bravo. Alles was ich wissen musste.
Patrick Fisher
Ich habe festgestellt, dass der Listener alle Array-Elemente in seinen Argumenten erhält, wenn Sie beim Auslösen eines benutzerdefinierten Ereignisses ein Array (mit einer Länge> 1) übergeben, sodass Sie am Ende eine nReihe von Argumenten erhalten.
vsync
Lösung für die Behandlung mehrerer Argumente im Listener:var eventData = [].slice.call(arguments).splice(1)
vsync
2
Gibt es eine Möglichkeit, dies ohne die #notifier-Dummy-Elemente zu tun? Wenn ich eine Javascript-Bibliothek hatte und wollte, dass ein Objekt ein Ereignis verfügbar macht, kann ich nicht sicher sein, welche Elemente auf der Seite vorhanden sind.
AaronLS
21

Ich denke schon ... es ist möglich, benutzerdefinierte Ereignisse zu "binden", wie (von: http://docs.jquery.com/Events/bind#typedatafn ):

 $("p").bind("myCustomEvent", function(e, myName, myValue){
      $(this).text(myName + ", hi there!");
      $("span").stop().css("opacity", 1)
               .text("myName = " + myName)
               .fadeIn(30).fadeOut(1000);
    });
    $("button").click(function () {
      $("p").trigger("myCustomEvent", [ "John" ]);
    });
Smoking
quelle
1
Ich suche nach einer Möglichkeit, ein Ereignis auszulösen und Abonnenten / Zuhörer darauf auszulösen. Nicht manuell auslösen, da das anhebende Objekt nicht weiß, wer zuhört ...
Per Hornshøj-Schierbeck
2
Dieser Code macht das, nicht wahr? myCustomEventWenn die Bindung oben angehoben wird, wird ein Listener hinzugefügt.
Sprintstar
15

Ich hatte eine ähnliche Frage, suchte aber tatsächlich nach einer anderen Antwort. Ich möchte ein benutzerdefiniertes Ereignis erstellen. Zum Beispiel, anstatt immer Folgendes zu sagen:

$('#myInput').keydown(function(ev) {
    if (ev.which == 13) {
        ev.preventDefault();
        // Do some stuff that handles the enter key
    }
});

Ich möchte es so abkürzen:

$('#myInput').enterKey(function() {
    // Do some stuff that handles the enter key
});

Trigger and Bind erzählen nicht die ganze Geschichte - dies ist ein JQuery-Plugin. http://docs.jquery.com/Plugins/Authoring

Die Funktion "enterKey" wird als Eigenschaft an jQuery.fn angehängt - dies ist der erforderliche Code:

(function($){
    $('body').on('keydown', 'input', function(ev) {
        if (ev.which == 13) {
            var enterEv = $.extend({}, ev, { type: 'enterKey' });
            return $(ev.target).trigger(enterEv);
        }
    });

    $.fn.enterKey = function(selector, data, fn) {
        return this.on('enterKey', selector, data, fn);
    };
})(jQuery);

http://jsfiddle.net/b9chris/CkvuJ/4/

Das Schöne daran ist, dass Sie Tastatureingaben bei Link-Listenern wie:

$('a.button').on('click enterKey', function(ev) {
    ev.preventDefault();
    ...
});

Änderungen: Aktualisiert, um den richtigen thisKontext ordnungsgemäß an den Handler zu übergeben und einen beliebigen Rückgabewert vom Handler an jQuery zurückzugeben (z. B. für den Fall, dass Sie das Ereignis abbrechen und sprudeln möchten). Aktualisiert, um ein ordnungsgemäßes jQuery-Ereignisobjekt an Handler zu übergeben, einschließlich Schlüsselcode und Möglichkeit zum Abbrechen von Ereignissen.

Alte jsfiddle: http://jsfiddle.net/b9chris/VwEb9/24/

Chris Moschini
quelle
Chris - Die Funktion hat perfekt funktioniert. Das einzige, was nicht funktioniert, ist der Zugriff auf diese Variable. Ich kann mit e.currentTarget darauf zugreifen. Ich habe mich jedoch gefragt, ob es einen effizienteren Weg gibt.
Addy
Guter Fang für diesen Fehler. Ja, wenn Sie Zeile 5 von Handler (ev) ändern. an: handler.call (this, ev); Dann ist dieses Argument so, wie es normalerweise in Standard-JQuery-Ereignishandlern ist. jsfiddle.net/b9chris/VwEb9
Chris Moschini
Kennen Sie auch eine Möglichkeit, dies umgekehrt umzusetzen? Ich versuche dieses Beispiel zu verwenden, aber mit scroll, was sich nicht verbreitet. Ich denke, anstatt die Zuhörer fest auf den Körper oncodieren zu lassen , brauche ich die Elemente, an die das Ereignis gebunden ist, um das Ereignis selbst auszulösen.
Gust van de Wal
Gust Ich bin mir nicht sicher, nach welchem ​​Szenario Sie fragen. könnte am besten als Frage mit Codebeispiel gestellt werden?
Chris Moschini
9

Es ist ein alter Beitrag, aber ich werde versuchen, ihn mit neuen Informationen zu aktualisieren.

Um benutzerdefinierte Ereignisse zu verwenden, müssen Sie es an ein DOM-Element binden und auslösen. Also musst du verwenden

Die Methode .on () verwendet einen Ereignistyp und eine Ereignisbehandlungsfunktion als Argumente. Optional kann es auch ereignisbezogene Daten als zweites Argument empfangen und die Ereignisbehandlungsfunktion auf das dritte Argument verschieben. Alle übergebenen Daten stehen der Ereignisbehandlungsfunktion in der Dateneigenschaft des Ereignisobjekts zur Verfügung. Die Ereignisbehandlungsfunktion empfängt immer das Ereignisobjekt als erstes Argument.

und

Die Methode .trigger () verwendet einen Ereignistyp als Argument. Optional kann es auch ein Array von Werten annehmen. Diese Werte werden als Argumente nach dem Ereignisobjekt an die Ereignisbehandlungsfunktion übergeben.

Der Code sieht folgendermaßen aus:

$(document).on("getMsg", {
    msg: "Hello to everyone",
    time: new Date()
}, function(e, param) {
    console.log( e.data.msg );
    console.log( e.data.time );
    console.log( param );
});

$( document ).trigger("getMsg", [ "Hello guys"] );

Schöne Erklärungen finden Sie hier und hier . Warum genau kann das nützlich sein? Ich habe in dieser hervorragenden Erklärung des Twitter-Ingenieurs herausgefunden, wie man es verwendet .

PS In einfachem Javascript können Sie dies mit dem neuen CustomEvent tun , aber Vorsicht vor IE- und Safari-Problemen.

Salvador Dali
quelle
3

So verfasse ich benutzerdefinierte Ereignisse:

var event = jQuery.Event('customEventName');
$(element).trigger(event);

Zugegeben, Sie könnten es einfach tun

$(element).trigger('eventname');

Aber die Art und Weise, wie ich geschrieben habe, ermöglicht es Ihnen zu erkennen, ob der Benutzer die Standardeinstellung verhindert hat oder nicht

var prevented = event.isDefaultPrevented();

Auf diese Weise können Sie die Anforderung Ihres Endbenutzers abhören, die Verarbeitung eines bestimmten Ereignisses zu beenden, z. B. wenn Sie auf ein Schaltflächenelement in einem Formular klicken, aber nicht möchten, dass das Formular im Fehlerfall veröffentlicht wird.


Normalerweise höre ich dann solche Ereignisse

$(element).off('eventname.namespace').on('eventname.namespace', function () {
    ...
});

Noch einmal, Sie könnten es einfach tun

$(element).on('eventname', function () {
    ...
});

Aber ich fand das immer etwas unsicher, besonders wenn Sie in einem Team arbeiten.

An folgendem ist nichts auszusetzen:

$(element).on('eventname', function () {});

Nehmen Sie jedoch an, dass ich dieses Ereignis aus irgendeinem Grund aufheben muss (stellen Sie sich eine deaktivierte Schaltfläche vor). Ich müsste dann tun

$(element).off('eventname', function () {});

Dadurch werden alle eventnameEreignisse aus entfernt $(element). Sie können nicht wissen, ob jemand in Zukunft auch ein Ereignis an dieses Element binden wird, und Sie würden dieses Ereignis auch versehentlich aufheben

Der sichere Weg, dies zu vermeiden, besteht darin, Ihre Ereignisse mit einem Namespace zu versehen

$(element).on('eventname.namespace', function () {});

Zuletzt haben Sie vielleicht bemerkt, dass die erste Zeile war

$(element).off('eventname.namespace').on('eventname.namespace', ...)

Ich persönlich binde immer ein Ereignis ab, bevor ich es binde, um sicherzustellen, dass derselbe Ereignishandler nie mehrmals aufgerufen wird (stellen Sie sich vor, dies war der Senden-Button auf einem Zahlungsformular und das Ereignis wurde fünfmal gebunden).

Luke Madhanga
quelle
schöner Ansatz. +1 für Namespace.
Aurovrata