Come verificare se l'evento click è già associato - JQuery


130

Sto associando un evento clic con un pulsante:

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

In uno scenario, questo viene chiamato più volte, quindi quando lo faccio triggervedo più chiamate ajax che voglio impedire.

Come faccio bindsolo se non è associato prima.


1
L'uomo, penso + Konrad Garus ha il più sicuro anwser ( stackoverflow.com/a/6930078/842768 ), pensare di cambiare la vostra risposta accettata. Saluti! AGGIORNAMENTO: Controlla anche il commento di Herbert Peters! Questo è l'approccio migliore.
giovannipds,

Per gli standard attuali, vedere la risposta di @A Morel di seguito ( stackoverflow.com/a/50097988/1163705 ). Meno codice e elimina tutte le congetture, gli algoritmi e / o la ricerca di elementi esistenti dall'equazione.
Xandor,

Risposte:


117

Aggiornamento 24 agosto '12 : in jQuery 1.8, non è più possibile accedere agli eventi dell'elemento utilizzando .data('events'). (Vedi questo bug per i dettagli.) È possibile accedere agli stessi dati con jQuery._data(elem, 'events')una struttura di dati interna, che è priva di documenti e quindi non garantita al 100% per rimanere stabile. Questo non dovrebbe, tuttavia, essere un problema e la riga pertinente del codice del plugin sopra può essere modificata come segue:

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

Gli eventi jQuery sono memorizzati in un oggetto dati chiamato events, quindi è possibile cercare in questo:

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

Sarebbe meglio, ovviamente, se potessi strutturare la tua applicazione in modo che questo codice venga chiamato solo una volta.


Questo potrebbe essere incapsulato in un plugin:

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

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

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

È quindi possibile chiamare:

var button = $('#myButton');
if (!button.isBound('click', onButtonClicked)) {
    button.click(onButtonClicked);
}

10
+1 per la modifica. Leggendo questo post oggi e sto usando 1.8. Grazie.
Gigi2m02

2
Grazie per la modifica. Questo è solo per i pulsanti o potrei anche usarlo per <a>? Perché quando provo dice jQuery._data()il seguente erroreTypeError: a is undefined
Houman,

Avrei avvolto la linea var data = jQuery._data(this[0], 'events')[type];in una presa di prova e restituito falso nella presa. Se nessun evento è associato a questo [0] di una chiamata a [tipo] provocherà alcune variazioni di "Impossibile ottenere il" clic "della proprietà di riferimento indefinito o nullo" e, ovviamente, ciò ti dice anche quello che devi sapere.
Robb Vandaveer,

Non sono sicuro che l'oggetto _data sia cambiato in jQuery 2.0.3 ma non ho potuto usare $ .inArray per questo. La funzione che si desidera confrontare si trova in una proprietà di ciascun elemento dati chiamato "gestore". L'ho modificato per usare una semplice istruzione e ho verificato l'equivalenza delle stringhe, cosa suppongo abbia fatto in Array. for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
Robb Vandaveer,

perché non spostare l'aggiornamento / la modifica all'inizio? ... È la risposta corretta effettiva.
Don Cheadle,

180

Un altro modo: contrassegnare tali pulsanti con una classe CSS e un filtro:

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

Nelle recenti versioni di jQuery sostituire bindcon on:

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

4
Eccellente! Grazie Konrad, questo colpisce il punto debole. Adoro approcci eleganti e semplici come questo. Sono abbastanza sicuro che sia anche più performante, dato che ogni singolo elemento (a cui sono già associati gestori di eventi click) non deve effettuare una ricerca completa in alcuni grandi eventi bucket confrontando ciascuno di essi. Nella mia implementazione ho chiamato la classe helper aggiunta "click-bound", che credo sia un'opzione più mantenibile: $ (". List-item: not (.click-bound)"). AddClass ("click-bound") ;
Nicholas Petersen,

1
Come indicato nella risposta accettata: "Sarebbe meglio, ovviamente, se tu potessi strutturare la tua applicazione in modo che questo codice venga chiamato solo una volta." Questo ci riesce.
Jeff Dege,

+1 nella mia situazione, off / on e bind / unbind ha impedito più chiamate. Questa era la soluzione che ha funzionato.
Jay,

2
Questa è la soluzione migliore per le prestazioni perché il codice può verificare la presenza della classe CSS e lo slip slip di già presente. Altre soluzioni eseguono un processo unbind-then-rebind con (almeno da quello che posso dire) più sovraccarico.
Phil Nicholas,

1
2 numeri. (quando lo si utilizza in un plug-in che può essere associato a più elementi) Non funzionerà quando si associa un evento di ridimensionamento all'oggetto finestra. Utilizzare i dati ('bound', 1) invece di addClass ('bound') è un altro approccio che funziona. Quando si distrugge l'evento potrebbe farlo mentre altre istanze dipendono da esso. Pertanto è consigliabile verificare se altre istanze sono ancora in uso.
Herbert Peters,

59

Se si utilizza jQuery 1.7+:

Puoi chiamare offprima on:

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

Altrimenti:

Puoi semplicemente scioglierlo al primo evento:

$('#myButton').unbind('click', onButtonClicked) //remove handler
              .bind('click', onButtonClicked);  //add handler

1
Questo non è esattamente una soluzione bella, ma sarebbe essere migliorata solo non associare quella del gestore: .unbind('click', onButtonClicked). Vedi il manuale
lonesomeday

16
In realtà puoi spaziare sui nomi dei tuoi gestori mentre li aggiungi, quindi la separazione diventa piuttosto semplice. $(sel).unbind("click.myNamespace");
Marc,

@lonesomeday è stato il tuo esempio usando "unbind" per mostrare qualcosa di diverso? .Unbind non è esattamente lo stesso di .off, quindi dovresti scrivere$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
Jim,

1
@Jim Vedrai che la domanda è stata modificata tre anni dopo il mio commento! (E anche nei minuti immediatamente successivi al mio commento. Il contenuto di cui stavo commentando non esiste più, motivo per cui probabilmente non sembra avere molto senso.)
Lonesomeday

@lonesomeday hmmm Non sono sicuro del motivo per cui è stato modificato, pensi che dovrei ripristinare?
Naftali aka Neal

8

Il modo migliore che vedo è usare live () o delegate () per catturare l'evento in un genitore e non in ogni elemento figlio.

Se il tuo pulsante si trova all'interno di un elemento #parent, puoi sostituire:

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

di

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

anche se #myButton non esiste ancora quando viene eseguito questo codice.


2
Metodi
obsoleti

8

Ho scritto un plugin molto piccolo chiamato "once" che lo fa. Eseguire off e on nell'elemento.

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

E semplicemente:

$(element).once('click', function(){
});

9
.one () eliminerà il gestore dopo la prima esecuzione.
Rodolfo Jorge Nemer Nogueira,

3
qui "una volta" serve per collegare un gestore una volta. "one" in jquery è per l'esecuzione una volta che non si collega una volta.
Shishir Arora,

1
So che sto arrivando dopo il fatto, ma non offrimuovi tutti i gestori per il tipo specificato? Significa che se ci fosse un gestore di clic separato non correlato ma sullo stesso articolo, anche quello non verrà rimosso?
Xandor,

basta usare uno spazio dei nomi sull'evento in modo che non lasci cadere tutti i gestori come: 'keypup.test123'
SemanticZen

6

Perché non usare questo

unbind() prima bind()

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

1
$('#myButton').off().on('click', onButtonClicked);funziona anche per me.
Veerakran Sereerungruangkul

Funziona per me ... soluzione gr8
imdadhusen,

Bellissimo. Funziona perfettamente per me per un pulsante che era già associato.
seanbreeden,

3

Ecco la mia versione:

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

Uso:

Utils.eventBoundToFunction(okButton, 'click', handleOkButtonFunction)

2

Basato sulla risposta di @ konrad-garus, ma usando data, poiché credo che classdovrebbe essere usato principalmente per lo styling.

if (!el.data("bound")) {
  el.data("bound", true);
  el.on("event", function(e) { ... });
}

2

Per evitare di controllare / associare / separare, puoi cambiare il tuo approccio! Perché non usi Jquery .on ()?

Poiché Jquery 1.7, .live (), .delegate () è obsoleto, ora puoi usare .on () per

Allega un gestore eventi per tutti gli elementi che corrispondono al selettore corrente, ora e in futuro

Vuol dire che puoi associare un evento a un elemento genitore che è ancora esistente e collegare elementi figli che siano presenti o meno!

Quando usi .on () in questo modo:

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

Prendi l'evento e fai clic sul genitore e cerca il figlio "#myButton" se esiste ...

Pertanto, quando rimuovi o aggiungi un elemento figlio, non devi preoccuparti se aggiungere o rimuovere l'associazione di eventi.


Questa è assolutamente la risposta migliore qui per gli standard attuali. Non credo che questa caratteristica di impostare il gestore sul genitore affinché guardi il bambino sia stata ben pubblicizzata ma è una soluzione piuttosto elegante. Stavo avendo un evento sparato più volte e ogni volta il numero totale di volte che sparava continuava ad aumentare. Questa singola linea ha fatto tutto il trucco e senza usare il .offquale credo rimuoverà gestori non correlati nel processo.
Xandor,

1

Provare:

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

A partire da giugno 2019, ho aggiornato la funzione (e funziona per quello che mi serve)

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

    if (data[type] === undefined || data.length === 0) {
        return false;
    }
    return true;
};

-2

JQuery ha una soluzione :

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

equivalente:

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

1
La tua risposta non è correlata alla domanda. La domanda riguarda l'associazione della funzione all'evento dell'elemento una sola volta.
Michał Zalewski,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.