Cómo verificar si el evento click ya está enlazado - JQuery

130

Estoy vinculando un evento de clic con un botón:

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

En un escenario, esto se llama varias veces, por lo que cuando hago una trigger, veo varias llamadas ajax que quiero evitar.

¿Cómo lo hago bindsolo si no está vinculado antes?

Polvoriento
fuente
1
Hombre, creo que + Konrad Garus tiene la respuesta más segura ( stackoverflow.com/a/6930078/842768 ), considera cambiar tu respuesta aceptada. ¡Salud! ACTUALIZACIÓN: ¡Compruebe + el comentario de Herbert Peters también! Ese es el mejor enfoque.
giovannipds
Para conocer los estándares actuales, consulte la respuesta de @A Morel a continuación ( stackoverflow.com/a/50097988/1163705 ). Menos codificación y elimina todas las conjeturas, algoritmos y / o búsqueda de elementos existentes fuera de la ecuación.
Xandor

Respuestas:

117

Actualización 24 de agosto '12 : en jQuery 1.8, ya no es posible acceder a los eventos del elemento utilizando .data('events'). (Consulte este error para obtener más detalles). Es posible acceder a los mismos datos con jQuery._data(elem, 'events')una estructura de datos interna, que no está documentada y, por lo tanto, no se garantiza que permanezca estable al 100%. Sin embargo, esto no debería ser un problema, y ​​la línea relevante del código del complemento anterior se puede cambiar a lo siguiente:

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

Los eventos jQuery se almacenan en un objeto de datos llamado events, por lo que puede buscar en esto:

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

Sería mejor, por supuesto, si pudiera estructurar su aplicación para que este código solo se llame una vez.


Esto podría encapsularse en un complemento:

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

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

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

Entonces puedes llamar a:

var button = $('#myButton');
if (!button.isBound('click', onButtonClicked)) {
    button.click(onButtonClicked);
}
solitario
fuente
10
+1 para la edición. Leyendo esta publicación hoy y estoy usando 1.8. Gracias.
Gigi2m02
2
Gracias por editar ¿Es solo para botones o también podría usarlo <a>? Porque cuando lo intento dice en jQuery._data()el siguiente errorTypeError: a is undefined
Houman
Envolvería la línea var data = jQuery._data(this[0], 'events')[type];en un intento de captura y devolvería falso en la captura. Si no hay eventos vinculados a esto [0], una llamada a [type] causará alguna variación de "No se puede obtener la propiedad 'clic' de referencia indefinida o nula" y obviamente eso también le dice lo que necesita saber.
Robb Vandaveer
No estoy seguro de si el objeto _data cambió en jQuery 2.0.3, pero no pude usar $ .inArray para esto. La función que desea comparar está en una propiedad de cada elemento de datos llamada "controlador". Lo modifiqué para usar una declaración simple para y verifiqué la equivalencia de cadena, lo que supongo que hizo en matriz. for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
Robb Vandaveer
¿por qué no mover su actualización / edición a la parte superior? ... Es la respuesta correcta real.
Don Cheadle
179

Una forma más: marque dichos botones con una clase CSS y filtro:

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

En versiones recientes de jQuery, reemplace bindcon on:

$('#myButton:not(.bound)').addClass('bound').on('click',  onButtonClicked);
Konrad Garus
fuente
44
¡Excelente! Gracias Konrad, esto da en el blanco. Me encantan los enfoques elegantes y simples como este. Estoy bastante seguro de que también es más eficiente, ya que cada elemento (que ya tiene adjuntos controladores de eventos de clic) no tiene que hacer una búsqueda completa en algunos grandes eventos comparando cada uno. En mi implementación, llamé a la clase auxiliar agregada "click-bound", que creo que es una opción más fácil de mantener: $ (". List-item: not (.click-bound)"). AddClass ("click-bound") ;
Nicholas Petersen
1
Como se indicó en la respuesta aceptada: "Sería mejor, por supuesto, si pudiera estructurar su aplicación para que este código solo se llame una vez". Esto logra eso.
Jeff Dege
+1 en mi situación, off / on y bind / unbind evitó múltiples llamadas. Esta fue la solución que funcionó.
Jay
2
Esta es la mejor solución para el rendimiento porque el código puede verificar la presencia de la clase CSS y el enlace deslizante de los ya presentes. Otra solución ejecuta un proceso de desvinculación y luego vinculación con (al menos desde lo que puedo decir) más sobrecarga.
Phil Nicholas
1
2 cuestiones (cuando se usa en un complemento que puede estar vinculado a varios elementos) No funcionará al vincular un evento de cambio de tamaño al objeto de la ventana. Usar datos ('enlazado', 1) en lugar de addClass ('enlazado') es otro enfoque que funciona. Al destruir el evento, podría hacerlo mientras otras instancias dependen de él. Por lo tanto, es aconsejable verificar si otras instancias todavía están en uso.
Herbert Peters
59

Si usa jQuery 1.7+:

Puedes llamar offantes on:

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

Si no:

Simplemente puede desvincularlo primer evento:

$('#myButton').unbind('click', onButtonClicked) //remove handler
              .bind('click', onButtonClicked);  //add handler
Naftali aka Neal
fuente
1
Esto no es exactamente una solución preciosa, pero podría mejorarse sólo desatar el manejador: .unbind('click', onButtonClicked). Vea el manual
lonesomeday
16
En realidad, puede asignar un espacio de nombres a sus controladores a medida que los agrega, luego la desvinculación se vuelve bastante simple. $(sel).unbind("click.myNamespace");
Marc
@lonesomeday ¿fue su ejemplo usando 'desvincular' para mostrar algo diferente? No es .unbind efectivamente lo mismo que .off, así que tendrías que escribir$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
Jim
1
@ Jim Verás que la pregunta fue editada tres años después de mi comentario. (Y también en los minutos inmediatamente después de mi comentario El contenido que estaba comentando ya no existe, por lo que, probablemente, no parece tener mucho sentido..)
lonesomeday
@lonesomeday hmmm No estoy seguro de por qué se editó, ¿crees que debería retroceder?
Naftali aka Neal
8

La mejor manera que veo es usar live () o delegate () para capturar el evento en un elemento primario y no en cada elemento secundario.

Si su botón está dentro de un elemento #parent, puede reemplazar:

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

por

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

incluso si #myButton aún no existe cuando se ejecuta este código.

Pablonete
fuente
2
Métodos obsoletos
Dobs
8

Escribí un plugin muy pequeño llamado "una vez" que hace eso. Ejecutar de vez en cuando en el elemento.

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

Y simplemente:

$(element).once('click', function(){
});
Rodolfo Jorge Nemer Nogueira
fuente
9
.one () soltará el controlador después de la primera ejecución.
Rodolfo Jorge Nemer Nogueira
3
aquí "una vez" es para adjuntar un controlador una vez. "one" en jquery es para ejecutar una vez y no adjuntar una vez.
Shishir Arora
1
Sé que vendré después del hecho, pero ¿no offelimina todos los controladores para el tipo dado? Es decir, si hubiera un controlador de clics separado sin relación pero en el mismo elemento, ¿no se eliminaría también?
Xandor
simplemente use un espacio de nombres en el evento para que no se caigan todos los controladores como: 'keypup.test123'
SemanticZen
6

¿Por qué no usar esto?

unbind() antes de bind()

$('#myButton').unbind().bind('click',  onButtonClicked);
Krillehbg
fuente
1
$('#myButton').off().on('click', onButtonClicked);También funciona para mí.
Veerakran Sereerungruangkul
Funciona para mí ... solución gr8
imdadhusen
Hermoso. Me funciona perfectamente para un botón que ya estaba vinculado.
seanbreeden
3

Aquí está mi versión:

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)
Yair Galler
fuente
2

Basado en la respuesta @ konrad-garus, pero usando data, ya que creo que classdebería usarse principalmente para el estilo.

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

Para evitar marcar / vincular / desvincular, ¡puede cambiar su enfoque! ¿Por qué no usas Jquery .on ()?

Como Jquery 1.7, .live (), .delegate () está en desuso, ahora puede usar .on () para

Adjunte un controlador de eventos para todos los elementos que coincidan con el selector actual, ahora y en el futuro

¡Significa que puede adjuntar un evento a un elemento primario que aún existe y adjuntar elementos secundarios si están presentes o no!

Cuando usas .on () así:

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

Captura el evento, haz clic en padre y busca hijo '#myButton' si existe ...

Por lo tanto, cuando elimine o agregue un elemento secundario, no tendrá que preocuparse de si desea agregar o eliminar el enlace del evento.

A. Morel
fuente
Esta es absolutamente la mejor respuesta aquí para los estándares actuales. No creo que esta característica de configurar el controlador en el padre para que vigile al niño se haya publicitado bien, pero es una solución bastante elegante. Estaba haciendo que un evento se disparara varias veces y cada vez la cantidad total de veces que disparaba seguía aumentando. Esta sola línea hizo todo el truco y, sin usar .off, creo que eliminaría los controladores no relacionados en el proceso.
Xandor
1

Tratar:

if (typeof($("#myButton").click) != "function") 
{
   $("#myButton").click(onButtonClicked);
}
Mukhtiar Zamin
fuente
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
fuente
0

A partir de junio de 2019, he actualizado la función (y está funcionando para lo que necesito)

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

    if (data[type] === undefined || data.length === 0) {
        return false;
    }
    return true;
};
Carlos Casalicchio
fuente
-2

JQuery tiene solución :

$( "#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 );
});
Veniamin
fuente
1
Su respuesta no está relacionada con la pregunta. La pregunta es acerca de vincular la función al evento del elemento solo una vez.
Michał Zalewski