Como verificar se o evento click já está vinculado - JQuery

130

Estou vinculando um evento de clique com um botão:

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

Em um cenário, isso está sendo chamado várias vezes; portanto, quando faço um trigger, vejo várias chamadas ajax que desejo impedir.

Como faço bindapenas se não estiver vinculado antes.

Empoeirado
fonte
1
Cara, acho que + Konrad Garus tem a resposta mais segura ( stackoverflow.com/a/6930078/842768 ), considere alterar sua resposta aceita. Felicidades! ATUALIZAÇÃO: Confira o comentário de + Herbert Peters também! Essa é a melhor abordagem.
Giovannipds
Para os padrões atuais, consulte a resposta de @A Morel abaixo ( stackoverflow.com/a/50097988/1163705 ). Menos codificação e retira todo o trabalho de adivinhação, algoritmos e / ou pesquisa de elementos existentes da equação.
Xandor 25/04/19

Respostas:

117

Atualização 24 de agosto '12 : No jQuery 1.8, não é mais possível acessar os eventos do elemento usando .data('events'). (Veja este bug para obter detalhes.) É possível acessar os mesmos dados com jQuery._data(elem, 'events')uma estrutura de dados interna, que não é documentada e, portanto, não tem 100% de garantia de permanecer estável. No entanto, isso não deve ser um problema, e a linha relevante do código do plug-in acima pode ser alterada para o seguinte:

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

Os eventos do jQuery são armazenados em um objeto de dados chamado events, para que você possa pesquisar:

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

Seria melhor, é claro, se você pudesse estruturar seu aplicativo para que esse código seja chamado apenas uma vez.


Isso pode ser encapsulado em um 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));
};

Você pode ligar para:

var button = $('#myButton');
if (!button.isBound('click', onButtonClicked)) {
    button.click(onButtonClicked);
}
lonesomeday
fonte
10
+1 para a edição. Lendo este post hoje e estou usando o 1.8. THX.
Gigi2m02 24/08/12
2
Obrigado pela edição. Isso é apenas para botões ou eu também poderia usá-lo <a>? Porque quando eu tento, diz sobre jQuery._data()o seguinte erroTypeError: a is undefined
Houman
Gostaria de quebrar a linha var data = jQuery._data(this[0], 'events')[type];em uma tentativa de captura e retornar falso na captura. Se nenhum evento estiver vinculado a esse [0], uma chamada para [type] causará alguma variação de "Não é possível obter propriedade 'clique' 'de referência indefinida ou nula" e, obviamente, isso também informa o que você precisa saber.
precisa saber é o seguinte
Não tenho certeza se o objeto _data foi alterado no jQuery 2.0.3, mas não pude usar $ .inArray para isso. A função que você deseja comparar está em uma propriedade de cada item de dados chamado "manipulador". Modifiquei-o para usar uma declaração simples e verifiquei a equivalência de cadeias, o que suponho que o inArray fez. for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
precisa saber é o seguinte
por que não mover sua atualização / edição para o topo? ... É a resposta correta.
Don Cheadle
179

Mais uma maneira - marque esses botões com uma classe e filtro CSS:

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

Nas versões recentes do jQuery, substitua bindpor on:

$('#myButton:not(.bound)').addClass('bound').on('click',  onButtonClicked);
Konrad Garus
fonte
4
Excelente! Graças à Konrad, isso atinge o ponto ideal. Eu amo abordagens elegantes e simples como essa. Tenho certeza de que também tem melhor desempenho, pois cada elemento (que já possui manipuladores de eventos de clique anexados) não precisa fazer uma pesquisa completa em alguns grandes intervalos de eventos comparando cada um. Na minha implementação, nomeei a classe auxiliar adicionada "clique-ligado", que eu acho que é uma opção mais sustentável: $ (". List-item: not (.click-bound)"). AddClass ("clique-bound") ;
Nicholas Petersen
1
Como foi afirmado na resposta aceita: "Seria melhor, é claro, se você pudesse estruturar seu aplicativo para que esse código seja chamado apenas uma vez". Isso faz isso.
Jeff Dege
+1 na minha situação, ativar / desativar e vincular / desvincular impediu várias chamadas. Essa foi a solução que funcionou.
21714 Jay
2
Essa é a melhor solução para o desempenho, porque o código pode verificar a presença da classe CSS e vincular a já existente. Outra solução executa um processo de desassociação e religação com (pelo menos pelo menos o que eu sei) mais sobrecarga.
9136 Phil Nicholas
1
2 questões. (ao usá-lo em um plug-in que pode ser associado a vários elementos) Não funcionará ao vincular um evento de redimensionamento ao objeto de janela. Usar dados ('bound', 1) em vez de addClass ('bound') é outra abordagem que funciona. Ao destruir o evento, pode ser feito enquanto outras instâncias dependem dele. Portanto, é recomendável verificar se outras instâncias ainda estão em uso.
Herbert Peters
59

Se você estiver usando o jQuery 1.7+:

Você pode ligar offantes on:

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

Se não:

Você pode simplesmente desatar o primeiro evento:

$('#myButton').unbind('click', onButtonClicked) //remove handler
              .bind('click', onButtonClicked);  //add handler
Naftali tcp Neal
fonte
1
Este não é exatamente uma solução agradável, mas seria melhorada apenas por liberando o um manipulador: .unbind('click', onButtonClicked). Veja o manual
lonesomeday
16
Na verdade, você pode dar um namespace aos seus manipuladores à medida que os adiciona, e a desagregação se torna bem simples. $(sel).unbind("click.myNamespace");
Marc
@lonesomeday foi seu exemplo usando 'unbind' para mostrar algo diferente? Não é .unbind efetivamente o mesmo que .off assim que você teria que escrever$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
Jim
1
@ Jim Você verá que a pergunta foi editada três anos após o meu comentário! (E também nos minutos imediatamente após o meu comentário O conteúdo que eu estava comentando sobre não existir mais, é por isso que, provavelmente, não parece fazer muito sentido..)
lonesomeday
@lonesomeday hmmm Não sei por que foi editado, você acha que devo reverter?
Naftali tcp Neal
8

A melhor maneira que vejo é usar live () ou delegate () para capturar o evento em um pai e não em cada elemento filho.

Se o botão estiver dentro de um elemento #parent, você poderá substituir:

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

de

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

mesmo que #myButton ainda não exista quando esse código for executado.

Pablonete
fonte
2
Métodos obsoletos
dobs
8

Eu escrevi um plugin muito pequeno chamado "once" que faz isso. Execute dentro e fora do elemento.

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

E simplesmente:

$(element).once('click', function(){
});
Rodolfo Jorge Nemer Nogueira
fonte
9
.one () eliminará o manipulador após a primeira execução.
Rodolfo Jorge Nemer Nogueira
3
aqui "uma vez" é para anexar um manipulador uma vez. "one" no jquery é para executar uma vez e não anexar uma vez.
Shishir Arora
1
Eu sei que estou entrando no caminho após o fato, mas não offremove todos os manipuladores para o tipo especificado? Ou seja, se houvesse um manipulador de cliques separado e não relacionado, mas no mesmo item, esse também não seria removido?
Xandor
basta usar um espaço para nome no evento para que ele não elimine todos os manipuladores como: 'keypup.test123'
SemanticZen
6

Por que não usar isso

unbind() antes bind()

$('#myButton').unbind().bind('click',  onButtonClicked);
Krillehbg
fonte
1
$('#myButton').off().on('click', onButtonClicked);também funciona para mim.
Veerakran Sereerungruangkul 03/04/19
Obras para mim ... solução gr8
imdadhusen
Lindo. Funciona perfeitamente para mim para um botão que já estava vinculado.
seanbreeden 8/01
3

Aqui está a minha versão:

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
fonte
2

Baseado na resposta @ konrad-garus, mas usando data, já que acredito que classdeve ser usado principalmente para estilizar.

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

Para evitar verificar / ligar / desligar, você pode mudar sua abordagem! Por que você não usa Jquery .on ()?

Como o Jquery 1.7, .live (), .delegate () está obsoleto, agora você pode usar .on () para

Anexe um manipulador de eventos para todos os elementos que correspondem ao seletor atual, agora e no futuro

Isso significa que você pode anexar um evento a um elemento pai que ainda existe e anexar elementos filhos, estejam eles presentes ou não!

Quando você usa .on () assim:

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

Você pega o evento, clique no pai e ele procura o filho '#myButton', se existir ...

Portanto, quando você remove ou adiciona um elemento filho, não precisa se preocupar em adicionar ou remover a ligação de evento.

A. Morel
fonte
Esta é absolutamente a melhor resposta para os padrões atuais. Não acho que esse recurso de definir o manipulador no pai para observar o filho tenha sido bem anunciado, mas é uma solução bastante elegante. Eu estava tendo um evento disparado várias vezes e cada vez a quantidade total de vezes que disparava continuava aumentando. Essa linha única executou todo o truque e, sem o uso .off, acredito que removeria manipuladores não relacionados no processo.
Xandor 25/04/19
1

Experimentar:

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

Em junho de 2019, atualizei a função (e ela está funcionando para o que eu preciso)

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

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

JQuery tem solução :

$( "#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
fonte
1
Sua resposta não está relacionada à pergunta. A pergunta é sobre a função de ligação ao evento do elemento apenas uma vez.
Michał Zalewski