Niestandardowe zdarzenia w jQuery?

180

Szukam wkładu, jak najlepiej zaimplementować niestandardową obsługę zdarzeń w jquery. Wiem, jak podłączyć zdarzenia z elementów dom, takich jak „kliknięcie” itp., Ale buduję małą bibliotekę / wtyczkę javascript, aby obsłużyć niektóre funkcje podglądu.

Mam uruchomiony skrypt, aby zaktualizować jakiś tekst w elemencie domowym z zestawu reguł i danych, które dostałem od użytkownika, ale teraz potrzebuję tego samego tekstu pokazanego w innych elementach, o których ten skrypt nie może wiedzieć. Potrzebuję dobrego wzorca, aby jakoś obserwować ten skrypt, który wytwarza potrzebny tekst.

Jak mam to zrobić? Czy przeoczyłem niektóre wbudowane funkcje jquery do podnoszenia / obsługi zdarzeń użytkownika, czy potrzebuję do tego wtyczki jquery? Jak myślisz, jaki jest najlepszy sposób / wtyczka, aby sobie z tym poradzić?

Per Hornshøj-Schierbeck
źródło
3
Istnieje inny program, knockoutjs.com, który może faktycznie rozwiązać mój stary problem, jeśli ktoś, kto patrzy na to pytanie, potrzebuje go.
Per Hornshøj-Schierbeck
2
Widząc, że wciąż otrzymuję reputację na podstawie tego pytania, ludzie wciąż muszą to czytać. Chcę tylko zaktualizować mój obecny wybór środowiska mvc po stronie klienta: angularjs.org
Per Hornshøj-Schierbeck
Mimo że całkowicie różni się od angular v1, Angular2 ( angular.io ) pokazuje tak wiele obietnic i będzie moim pierwszym wyborem, gdy nie będzie wersji beta / rc.
Per Hornshøj-Schierbeck 18.04.16

Odpowiedzi:

105

Spójrz na to:

(przedrukowano z wygasłej strony blogu http://jamiethompson.co.uk/web/2008/06/17/publish-subscribe-with-jquery/ na podstawie zarchiwizowanej wersji pod adresem http://web.archive.org/web /20130120010146/http://jamiethompson.co.uk/web/2008/06/17/publish-subscribe-with-jquery/ )


Publikuj / subskrybuj za pomocą jQuery

17 czerwca 2008 r

Mając na uwadze napisanie interfejsu użytkownika jQuery zintegrowanego z funkcją offline Google Gears, bawiłem się kodem, aby sprawdzić status połączenia sieciowego za pomocą jQuery.

Obiekt wykrywania sieci

Podstawowa przesłanka jest bardzo prosta. Tworzymy instancję obiektu wykrywającego sieć, który będzie sondować adres URL w regularnych odstępach czasu. Jeśli te żądania HTTP zakończą się niepowodzeniem, możemy założyć, że połączenie sieciowe zostało utracone lub serwer jest w tej chwili niedostępny.

$.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]);
            }
        });
    };
};

Możesz zobaczyć wersję demo tutaj. Ustaw przeglądarkę do pracy w trybie offline i zobacz, co się stanie… nie, to nie jest bardzo ekscytujące.

Trigger and Bind

To, co jest ekscytujące (a przynajmniej to, co mnie ekscytuje), to metoda, dzięki której status jest przekazywany za pośrednictwem aplikacji. Natknąłem się na szeroko dyskutowaną metodę implementacji systemu pub / sub przy użyciu metod wyzwalających i wiążących jQuery.

Kod demonstracyjny jest bardziej tępy niż powinien. Obiekt wykrywania sieci publikuje zdarzenia „status” w dokumencie, który aktywnie nasłuchuje, a z kolei publikuje zdarzenia „powiadomienie” wszystkim subskrybentom (więcej na ten temat później). Powodem tego jest fakt, że w rzeczywistej aplikacji prawdopodobnie istniałaby pewna logika kontrolująca, kiedy i jak publikowane są zdarzenia „powiadomień”.

$(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
    */
});

Ze względu na DOM podejście centryczne jQuery zdarzenia są publikowane w elementach DOM (wyzwalanych). Może to być obiekt okna lub dokumentu dla zdarzeń ogólnych lub można wygenerować obiekt jQuery za pomocą selektora. Podejście, które podjąłem w wersji demonstracyjnej, polega na stworzeniu podejścia do definiowania subskrybentów o niemal przestrzeni nazw.

Elementy DOM, które mają być subskrybentami, są po prostu klasyfikowane za pomocą „subscriber” i „networkDetection”. Następnie możemy publikować zdarzenia tylko do tych elementów (których w demie jest tylko jeden), uruchamiając powiadomienie$(“.subscriber.networkDetection”)

#notifierDiv, który jest częścią .subscriber.networkDetectiongrupy abonentów ma wtedy anonimową funkcję związaną z nim, skutecznie działającą w charakterze słuchacza.

$('#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");
        }
    };
});

A więc proszę bardzo. To wszystko jest dość gadatliwe, a mój przykład wcale nie jest ekscytujący. Nie pokazuje też nic ciekawego, co można zrobić za pomocą tych metod, ale jeśli ktoś jest zainteresowany kopaniem źródła, nie krępuj się. Cały kod jest wbudowany w nagłówku strony demonstracyjnej

Vitor Silva
źródło
Właśnie tego szukałem. Prawdopodobnie ma rację, że jest to właściwa droga, ale nie mogę przestać myśleć, że jquery potrzebuje bezpośredniego wsparcia lub wtyczki, aby poradzić sobie z tym z wdziękiem. Poczekam chwilę i zobaczę, czy są inne problemy, zanim
oznaczę
6
Czy to skądś cytat? Jeśli tak, proszę podać swoje źródło i podać link, jeśli nadal istnieje.
Aryeh Leib Taurog
1
Tak, to cytat, w historii edycji znajduje się ten link , obecnie jest niedostępny. Zarchiwizowana strona jest tutaj .
Stano
1
Ben Alman napisał małą wtyczkę jQuery pub sub . Jest niezwykle elegancki.
Barney
127

Link podany w zaakceptowanej odpowiedzi pokazuje dobry sposób na implementację systemu pub / sub za pomocą jQuery, ale kod był nieco trudny do odczytania, więc oto moja uproszczona wersja kodu:

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
źródło
10
Naprawdę podoba mi się ta odpowiedź. Każdy może czytać dokumenty na trigger () bind () (i on ()), ale ta odpowiedź stanowi konkretny przykład tworzenia magistrali zdarzeń (pub / sub system) przy użyciu jquery.
lostdorje
1
Brawo. Wszystko, co musiałem wiedzieć.
Patrick Fisher
Odkryłem, że jeśli przekażesz tablicę (gdzie długość> 1), gdy uruchomisz zdarzenie niestandardowe, to nasłuchiwanie otrzyma wszystkie elementy tablicy w swoich argumentach, więc skończysz na nliczbie argumentów.
vsync
rozwiązanie obsługi wielu argumentów w detektorze:var eventData = [].slice.call(arguments).splice(1)
vsync
2
Czy można to zrobić bez elementów manekina #notifier? Gdybym miał bibliotekę JavaScript i chciałbym, aby obiekt ujawnił zdarzenie, nie jestem pewien, jakie elementy istnieją na stronie.
AaronLS
21

Myślę, że tak ... możliwe jest „powiązanie” niestandardowych zdarzeń, takich jak (z: 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" ]);
    });
Tuxified
źródło
1
Szukam sposobu na podniesienie zdarzenia i wywołanie go przez subskrybentów / słuchaczy. Nie wyzwalaj go ręcznie, ponieważ obiekt podnoszący nie wie, kto słucha ...
Per Hornshøj-Schierbeck
2
Ten kod to robi, prawda? myCustomEventjest podniesiony, powiązanie u góry dodaje detektora.
Sprintstar
15

Miałem podobne pytanie, ale tak naprawdę szukałem innej odpowiedzi; Chcę utworzyć niestandardowe wydarzenie. Na przykład zamiast zawsze tak mówić:

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

Chcę to skrócić do tego:

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

wyzwalaj i wiąż nie opowiadaj całej historii - jest to wtyczka JQuery. http://docs.jquery.com/Plugins/Authoring

Funkcja „enterKey” zostaje dołączona jako właściwość do jQuery.fn - jest to wymagany kod:

(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/

Jedną z zalet powyższego jest to, że możesz z wdziękiem obsługiwać klawiaturę w słuchaczach linków, takich jak:

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

Edycje: Zaktualizowano, aby poprawnie przekazać właściwy thiskontekst do procedury obsługi i zwrócić dowolną wartość zwracaną z procedury obsługi do jQuery (na przykład w przypadku, gdy chciałeś anulować zdarzenie i propagację). Zaktualizowano, aby przekazać odpowiedni obiekt zdarzenia jQuery do procedur obsługi, w tym kod klucza i możliwość anulowania zdarzenia.

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

Chris Moschini
źródło
Chris - Funkcja działała idealnie .. jedyną rzeczą, która nie działa, jest dostęp do tej zmiennej .. jestem w stanie uzyskać do niej dostęp za pomocą e.currentTarget .. ale zastanawiałem się, czy istnieje bardziej wydajny sposób.
Addy
Dobrze złap ten błąd. Tak, jeśli zmienisz wiersz 5 z modułu obsługi (ev); do: handler.call (this, ev); wtedy ten argument będzie taki, jak zwykle w standardowych procedurach obsługi zdarzeń jquery. jsfiddle.net/b9chris/VwEb9
Chris Moschini
Czy znasz także sposób na wdrożenie tego na odwrót? Próbuję użyć tego przykładu, ale z scroll, który się nie propaguje. Myślę, że zamiast zakodować słuchaczy w ciele, potrzebuję elementów, które onzwiązały się z nimi, by sami odpalili to wydarzenie.
Gust van de Wal
Gust Nie jestem pewien scenariusza, o który pytasz; najlepiej zadać jako pytanie z próbką kodu?
Chris Moschini,
9

To stary post, ale postaram się go zaktualizować o nowe informacje.

Aby użyć niestandardowych zdarzeń, musisz powiązać go z jakimś elementem DOM i uruchomić go. Więc musisz użyć

Metoda .on () przyjmuje jako argument typ zdarzenia i funkcję obsługi zdarzenia. Opcjonalnie może również odbierać dane związane ze zdarzeniami jako drugi argument, co powoduje przeniesienie funkcji obsługi zdarzeń do trzeciego argumentu. Wszelkie przekazywane dane będą dostępne dla funkcji obsługi zdarzeń we właściwości danych obiektu zdarzenia. Funkcja obsługi zdarzeń zawsze odbiera obiekt zdarzenia jako swój pierwszy argument.

i

Metoda .trigger () przyjmuje jako argument typ zdarzenia. Opcjonalnie może również przyjmować tablicę wartości. Wartości te zostaną przekazane do funkcji obsługi zdarzeń jako argumenty za obiektem zdarzenia.

Kod wygląda następująco:

$(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"] );

Ładne wyjaśnienie można znaleźć tutaj i tutaj . Dlaczego dokładnie to może być przydatne? Znalazłem, jak go używać w tym doskonałym wyjaśnieniu inżyniera Twittera .

PS W zwykłym javascript możesz to zrobić za pomocą nowego CustomEvent , ale uważaj na problemy z IE i Safari.

Salvador Dali
źródło
3

Oto, w jaki sposób tworzę zdarzenia niestandardowe:

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

To prawda, że ​​możesz po prostu zrobić

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

Ale sposób, w jaki napisałem, pozwala wykryć, czy użytkownik uniemożliwił domyślne działanie, czy nie

var prevented = event.isDefaultPrevented();

Pozwala to wysłuchać żądania użytkownika końcowego, aby przestał przetwarzać określone zdarzenie, na przykład jeśli klikniesz element przycisku w formularzu, ale nie chcesz, aby formularz wysyłał wiadomość w przypadku wystąpienia błędu.


Następnie zwykle słucham takich wydarzeń

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

Jeszcze raz możesz po prostu zrobić

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

Ale zawsze uważałem to za niebezpieczne, szczególnie jeśli pracujesz w zespole.

Nie ma nic złego w następujących kwestiach:

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

Załóżmy jednak, że muszę usunąć to zdarzenie z jakiegokolwiek powodu (wyobraź sobie wyłączony przycisk). Musiałbym wtedy zrobić

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

Spowoduje to usunięcie wszystkich eventnamezdarzeń z $(element). Nie możesz wiedzieć, czy ktoś w przyszłości również przypisze wydarzenie do tego elementu, a także niechcący rozpiszesz to wydarzenie

Bezpiecznym sposobem na uniknięcie tego jest przestrzeń nazw zdarzeń

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

Wreszcie możesz zauważyć, że pierwsza linia była

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

Ja osobiście zawsze unbind zdarzenie przed wiążące go tylko upewnić się, że nigdy taka sama obsługi zdarzeń jest wywoływana wielokrotnie (wyobrazić to było złożyć przycisk na formularzu płatności i zdarzenie zostało zobowiązane 5 razy)

Luke Madhanga
źródło
niezłe podejście. +1 dla przestrzeni nazw.
Aurovrata