Jak sprawdzić, czy zdarzenie kliknięcia jest już powiązane - JQuery

132

Zdarzenie kliknięcia wiążę przyciskiem:

$('#myButton').bind('click',  onButtonClicked);

W jednym scenariuszu jest to wywoływane wiele razy, więc kiedy robię trigger, widzę wiele wywołań Ajax, którym chcę zapobiec.

Jak mam bindtylko wtedy, gdy nie jest to związane wcześniej.

Zakurzony
źródło
1
Człowieku, myślę, że + Konrad Garus ma najbezpieczniejszą odpowiedź ( stackoverflow.com/a/6930078/842768 ), rozważ zmianę zaakceptowanej odpowiedzi. Twoje zdrowie! AKTUALIZACJA: Sprawdź także komentarz Herberta Petersa! To najlepsze podejście.
giovannipds
Aby zapoznać się z aktualnymi standardami, zobacz odpowiedź @A Morel poniżej ( stackoverflow.com/a/50097988/1163705 ). Mniej kodowania i eliminuje wszystkie zgadywania, algorytmy i / lub wyszukiwanie istniejących elementów z równania.
Xandor

Odpowiedzi:

118

Aktualizacja 24 sierpnia '12 : W jQuery 1.8 nie można już uzyskać dostępu do zdarzeń elementu za pomocą .data('events'). (Zobacz ten błąd, aby uzyskać szczegółowe informacje.) Możliwe jest uzyskanie dostępu do tych samych danych za jQuery._data(elem, 'events')pomocą wewnętrznej struktury danych, która jest nieudokumentowana, a zatem nie gwarantuje 100% stabilności. Nie powinno to jednak stanowić problemu, a odpowiedni wiersz powyższego kodu wtyczki można zmienić na następujący:

var data = jQuery._data(this[0], 'events')[type];

Zdarzenia jQuery są przechowywane w obiekcie danych o nazwie events, więc możesz szukać w tym:

var button = $('#myButton');
if (-1 !== $.inArray(onButtonClicked, button.data('events').click)) {
    button.click(onButtonClicked);
}

Oczywiście najlepiej byłoby, gdybyś mógł ustrukturyzować swoją aplikację, tak aby ten kod był wywoływany tylko raz.


Można to umieścić we wtyczce:

$.fn.isBound = function(type, fn) {
    var data = this.data('events')[type];

    if (data === undefined || data.length === 0) {
        return false;
    }

    return (-1 !== $.inArray(fn, data));
};

Możesz wtedy zadzwonić:

var button = $('#myButton');
if (!button.isBound('click', onButtonClicked)) {
    button.click(onButtonClicked);
}
samotny dzień
źródło
10
+1 do zmiany. Czytając ten post dzisiaj i używam 1.8. Dzięki.
Gigi2m02
2
Dzięki za edycję. Czy to tylko dla przycisków, czy też mogę go użyć <a>? Ponieważ kiedy spróbuję, mówi o jQuery._data()następującym błędzieTypeError: a is undefined
Houman
Zawinąłbym wiersz var data = jQuery._data(this[0], 'events')[type];w try catch i zwrócił false w catch. Jeśli żadne zdarzenia nie są powiązane z tym [0], to wywołanie [type] spowoduje pewną odmianę „Nie można pobrać właściwości 'kliknięcie' niezdefiniowanego lub zerowego odwołania” i oczywiście to również mówi ci, co musisz wiedzieć.
Robb Vandaveer
Nie jestem pewien, czy obiekt _data zmienił się w jQuery 2.0.3, ale nie mogłem użyć do tego $ .inArray. Funkcja, którą chcesz porównać, znajduje się we właściwości każdego elementu danych o nazwie „procedura obsługi”. Zmodyfikowałem go tak, aby używał prostej instrukcji for i sprawdziłem równoważność ciągów, co, jak zakładam, zrobiło w tablicy. for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
Robb Vandaveer
dlaczego nie przenieść aktualizacji / edycji na górę? ... To właściwa poprawna odpowiedź.
Don Cheadle
183

Jeszcze jedno - oznacz takie przyciski klasą CSS i filtrem:

$('#myButton:not(.bound)').addClass('bound').bind('click',  onButtonClicked);

W ostatnich wersjach jQuery zastąpić bindz on:

$('#myButton:not(.bound)').addClass('bound').on('click',  onButtonClicked);
Konrad Garus
źródło
4
Świetny! Dzięki Konrad, to trafia w sedno. Uwielbiam takie eleganckie i proste podejścia. Jestem prawie pewien, że jest również bardziej wydajny, ponieważ każdy element (który ma już dołączone programy obsługi zdarzeń kliknięcia) nie musi przeprowadzać pełnego wyszukiwania w jakimś dużym zasobniku zdarzeń, porównując każdy z nich. W mojej implementacji nazwa dodanej klasy pomocniczej jest „związana z kliknięciem”, co moim zdaniem jest łatwiejszą w utrzymaniu opcją: $ („. List-item: not (.click-bound)”). AddClass („click-bound”) ;
Nicholas Petersen
1
Jak stwierdzono w zaakceptowanej odpowiedzi: „Oczywiście najlepiej byłoby, gdybyś mógł ustrukturyzować swoją aplikację tak, aby ten kod był wywoływany tylko raz”. To osiąga ten cel.
Jeff Dege
+1 w mojej sytuacji, off / on i bind / unbind zapobiegły wielu wywołaniom. To było rozwiązanie, które zadziałało.
Jay
2
Jest to lepsze rozwiązanie pod względem wydajności, ponieważ kod może sprawdzić obecność klasy CSS i powiązanie poślizgu już istniejącego. Inne rozwiązanie wykonuje proces unbind-then-rebind z (przynajmniej z tego, co mogę powiedzieć) większym narzutem.
Phil Nicholas,
1
2 numery. (gdy jest używany we wtyczce, która może być powiązana z kilkoma elementami) Nie będzie działać podczas wiązania zdarzenia zmiany rozmiaru z obiektem okna. Użyj danych („związane”, 1) zamiast addClass („powiązane”) to kolejne podejście, które działa. Podczas niszczenia wydarzenia może to zrobić, podczas gdy inne instancje od niego zależą. Dlatego zalecane jest sprawdzenie, czy inne instancje są nadal używane.
Herbert Peters
60

Jeśli używasz jQuery 1.7+:

Możesz zadzwonić offwcześniej on:

$('#myButton').off('click', onButtonClicked) // remove handler
              .on('click', onButtonClicked); // add handler

Jeśli nie:

Możesz po prostu rozpiąć pierwsze zdarzenie:

$('#myButton').unbind('click', onButtonClicked) //remove handler
              .bind('click', onButtonClicked);  //add handler
Naftali alias Neal
źródło
1
To nie jest dokładnie to piękny rozwiązanie, ale byłoby poprawić tylko Rozpinanie jedynego obsługi: .unbind('click', onButtonClicked). Zobacz instrukcję
lonesomeday,
16
W rzeczywistości możesz określić przestrzeń nazw swoich programów obsługi podczas ich dodawania, a wtedy odłączanie staje się całkiem proste. $(sel).unbind("click.myNamespace");
Marc
@lonesomeday był Twój przykład użycia „unbind”, aby pokazać coś innego? Czy .unbind nie jest faktycznie tym samym, co .off, więc musiałbyś napisać$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
Jim
1
@Jim Zobaczysz, że pytanie zostało zredagowane trzy lata po moim komentarzu! (A także w minutach zaraz po moim komentarzu. Treść, którą komentowałem, już nie istnieje, dlatego prawdopodobnie nie ma to większego sensu.)
lonesomeday
@lonesomeday hmmm Nie jestem pewien, dlaczego został edytowany, myślisz, że powinienem wycofać?
Naftali aka Neal
8

Najlepszym sposobem, jaki widzę, jest użycie live () lub delegate () do przechwytywania zdarzenia w rodzicu, a nie w każdym elemencie podrzędnym.

Jeśli twój przycisk znajduje się wewnątrz elementu #parent, możesz zastąpić:

$('#myButton').bind('click', onButtonClicked);

przez

$('#parent').delegate('#myButton', 'click', onButtonClicked);

nawet jeśli #myButton jeszcze nie istnieje, gdy ten kod jest wykonywany.

Pablonete
źródło
2
Przestarzałe metody
data data urodzenia
8

Napisałem bardzo małą wtyczkę o nazwie „raz”, która to robi. Wykonywanie i włączanie w elemencie.

$.fn.once = function(a, b) {
    return this.each(function() {
        $(this).off(a).on(a,b);
    });
};

Po prostu:

$(element).once('click', function(){
});
Rodolfo Jorge Nemer Nogueira
źródło
9
.one () usunie program obsługi po pierwszym wykonaniu.
Rodolfo Jorge Nemer Nogueira
3
tutaj „raz” służy do jednorazowego dołączenia programu obsługi. „jedynka” w jquery oznacza uruchomienie raz, a nie dołączenie raz.
Shishir Arora
1
Wiem, że wchodzę w drogę po fakcie, ale czy nie offusuwa wszystkich programów obsługi dla danego typu? Czyli gdyby istniał oddzielny moduł obsługi kliknięć niepowiązany, ale dotyczący tego samego elementu, czy ten również nie zostałby usunięty?
Xandor
po prostu użyj przestrzeni nazw w zdarzeniu, aby nie porzucić wszystkich programów obsługi, takich jak: 'keypup.test123'
SemanticZen
6

Dlaczego nie skorzystać z tego

unbind() przed bind()

$('#myButton').unbind().bind('click',  onButtonClicked);
Krillehbg
źródło
1
$('#myButton').off().on('click', onButtonClicked);działa również dla mnie.
Veerakran Sereerungruangkul
Działa dla mnie ... rozwiązanie gr8
imdadhusen
Piękny. U mnie działa idealnie na guzik, który był już oprawiony.
seanbreeden
3

Oto moja wersja:

Utils.eventBoundToFunction = function (element, eventType, fCallback) {
    if (!element || !element.data('events') || !element.data('events')[eventType] || !fCallback) {
        return false;
    }

    for (runner in element.data('events')[eventType]) {
        if (element.data('events')[eventType][runner].handler == fCallback) {
            return true;
        }

    }

    return false;
};

Stosowanie:

Utils.eventBoundToFunction(okButton, 'click', handleOkButtonFunction)
Yair Galler
źródło
2

Na podstawie odpowiedzi @ konrad-garus, ale używam data, ponieważ uważam, że classpowinno być używane głównie do stylizacji.

if (!el.data("bound")) {
  el.data("bound", true);
  el.on("event", function(e) { ... });
}
ariel
źródło
2

Aby uniknąć sprawdzania / wiązania / usuwania powiązań, możesz zmienić swoje podejście! Dlaczego nie używasz Jquery .on ()?

Ponieważ Jquery 1.7, .live (), .delegate () są przestarzałe, teraz możesz używać .on () do

Dołącz procedurę obsługi zdarzeń dla wszystkich elementów, które pasują do bieżącego selektora, teraz i w przyszłości

Oznacza to, że możesz dołączyć zdarzenie do elementu nadrzędnego, który wciąż istnieje i dołączyć elementy potomne, czy są obecne, czy nie!

Kiedy używasz .on () w ten sposób:

$('#Parent').on('click', '#myButton'  onButtonClicked);

Wychwytujesz zdarzenie, klikasz na rodzica i wyszukujesz podrzędny „#myButton”, jeśli istnieje ...

Dlatego kiedy usuwasz lub dodajesz element podrzędny, nie musisz się martwić, czy dodać lub usunąć powiązanie zdarzenia.

A. Morel
źródło
To zdecydowanie najlepsza odpowiedź na obecne standardy. Nie sądzę, aby ta funkcja ustawiania opiekuna na rodzicu, aby obserwował dziecko, była dobrze reklamowana, ale jest raczej eleganckim rozwiązaniem. Zdarzało mi się wiele razy i za każdym razem sumaryczna liczba ich wystrzeli rosła. Ta pojedyncza linia załatwiła całą sprawę i bez użycia .offktórej, jak sądzę, usunęłaby niepowiązane procedury obsługi w procesie.
Xandor
1

Próbować:

if (typeof($("#myButton").click) != "function") 
{
   $("#myButton").click(onButtonClicked);
}
Mukhtiar Zamin
źródło
0
if ($("#btn").data('events') != undefined && $("#btn").data('events').click != undefined) {
    //do nothing as the click event is already there
} else {
    $("#btn").click(function (e) {
        alert('test');
    });
}
Bishoy Hanna
źródło
0

Od czerwca 2019 r. Zaktualizowałem tę funkcję (i działa zgodnie z potrzebami)

$.fn.isBound = function (type) {
    var data = $._data($(this)[0], 'events');

    if (data[type] === undefined || data.length === 0) {
        return false;
    }
    return true;
};
Carlos Casalicchio
źródło
-2

JQuery ma rozwiązanie :

$( "#foo" ).one( "click", function() {
  alert( "This will be displayed only once." );
}); 

równowartość:

$( "#foo" ).on( "click", function( event ) {
  alert( "This will be displayed only once." );
  $( this ).off( event );
});
Veniamin
źródło
1
Twoja odpowiedź nie jest związana z pytaniem. Pytanie dotyczy tylko jednokrotnego powiązania funkcji ze zdarzeniem elementu.
Michał Zalewski