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.


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);
}

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);

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

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.


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(){
});

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);

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)

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

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.


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);
}

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');
    });
}

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;
};

-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 );
});

1
Twoja odpowiedź nie jest związana z pytaniem. Pytanie dotyczy tylko jednokrotnego powiązania funkcji ze zdarzeniem elementu.
Michał Zalewski
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.