Eventos de clique em jQuery disparando várias vezes


283

Estou tentando escrever um jogo de vídeo pôquer em Javascript como uma maneira de obter o básico, e encontrei um problema em que os manipuladores de eventos do jQuery click estão disparando várias vezes.

Eles estão anexados aos botões para fazer uma aposta e funciona bem para fazer uma aposta na primeira mão durante um jogo (disparando apenas uma vez); mas, ao apostar pela segunda mão, ele dispara o evento click duas vezes cada vez que um botão de aposta ou de aposta é pressionado (portanto, o dobro do valor correto é apostado para cada pressionamento). No geral, segue esse padrão pelo número de vezes que o evento click é acionado ao pressionar um botão de aposta uma vez - onde o i - ésimo termo da sequência é para as apostas da i- mão desde o início do jogo: 1, 2, 4 , 7, 11, 16, 22, 29, 37, 46, que parece ser n (n + 1) / 2 + 1 para o que vale a pena - e eu não era inteligente o suficiente para descobrir isso, usei o OEIS . :)

Aqui está a função com os manipuladores de eventos click que estão agindo; espero que seja fácil de entender (deixe-me saber se não, eu também quero melhorar):

/** The following function keeps track of bet buttons that are pressed, until place button is pressed to place bet. **/
function pushingBetButtons() {
    $("#money").text("Money left: $" + player.money); // displays money player has left

    $(".bet").click(function() {
        var amount = 0; // holds the amount of money the player bet on this click
        if($(this).attr("id") == "bet1") { // the player just bet $1
            amount = 1;
        } else if($(this).attr("id") == "bet5") { // etc.
            amount = 5;
        } else if($(this).attr("id") == "bet25") {
            amount = 25;
        } else if($(this).attr("id") == "bet100") {
            amount = 100;
        } else if($(this).attr("id") == "bet500") {
            amount = 500;
        } else if($(this).attr("id") == "bet1000") {
            amount = 1000;
        }
        if(player.money >= amount) { // check whether the player has this much to bet
            player.bet += amount; // add what was just bet by clicking that button to the total bet on this hand
            player.money -= amount; // and, of course, subtract it from player's current pot
            $("#money").text("Money left: $" + player.money); // then redisplay what the player has left
        } else {
            alert("You don't have $" + amount + " to bet.");
        }
    });

    $("#place").click(function() {
        if(player.bet == 0) { // player didn't bet anything on this hand
            alert("Please place a bet first.");
        } else {
            $("#card_para").css("display", "block"); // now show the cards
            $(".card").bind("click", cardClicked); // and set up the event handler for the cards
            $("#bet_buttons_para").css("display", "none"); // hide the bet buttons and place bet button
            $("#redraw").css("display", "block"); // and reshow the button for redrawing the hand
            player.bet = 0; // reset the bet for betting on the next hand
            drawNewHand(); // draw the cards
        }
    });
}

Informe-me se você tem alguma idéia ou sugestão, ou se a solução para o meu problema é semelhante a uma solução para outro problema aqui (observei muitos tópicos com o mesmo título e não tive sorte em encontrar uma solução que pudesse funcionar) para mim).


var amount = parseInt(this.id.replace(/[^\d]/g,''),10);E se você usar a mesma propriedade de um elemento mais de uma vez, armazene em cache essa propriedade, não continue pesquisando. A consulta é cara.
David diz que restabelece Monica

Obrigado pela resposta e pela dica sobre as propriedades de armazenamento em cache. Defino player.money e player.bet para variáveis ​​locais money e bet dentro dessa função e manipulei-as em vez disso, e alterarei o resto do meu código para fazer isso também. :) Se você tiver tempo, também poderá explicar o que sugeriu inicialização de quantidade está fazendo; parece uma expressão regular, mas não consigo entender isso facilmente.
22813 Gregory Fowler

@GregoryFowler - não relacionado à sua pergunta, mas ... pode valer a pena examinar uma declaração de troca de javascript.
Clayton

2
Cara, sua função é colocar um manipulador de cliques cada vez que é chamado. Se você invocá-lo em cada rodada, na segunda rodada, você terá dois manipuladores e assim por diante. Cada manipulador faz seu trabalho e na rodada 100 você recebe 100 alertas.
Marco Faustinelli

Isso acontece porque, em algum lugar do seu código, você está religando o manipulador de eventos sem primeiro desvinculá-lo. Veja esta pergunta para um cenário semelhante e uma boa explicação.
jpaugh

Respostas:


526

Para garantir que um clique apenas as ações sejam usadas uma vez:

$(".bet").unbind().click(function() {
    //Stuff
});

31
Ok, onde você estava ontem, Rob? ;) Era exatamente isso que eu estava procurando, não sei por que não encontrei antes. Mas ainda era uma bênção disfarçada, porque eu aprendi muitas outras coisas.
precisa

1
;) Desculpe. Girei minhas rodas por alguns dias também. Eu ainda estou procurando por uma razão lógica para explicar porque isso acontece mesmo em primeiro lugar.
21413 Rob

6
Homem depois de tantas coisas que isso fez. Exceto no meu caso, usei o equivalente a: $ (". Bet"). Off (). On ()
MadTurki

7
Como jroi_web diz abaixo, este método está obsoleto. Consulte a resposta da @ trolle para obter uma solução mais recente.
Pascal

Um manipulador de cliques não é uma coisa descartável. Especialmente quando é o volumoso se-se-se mostrado na pergunta. Você deve anexá-lo e deixá-lo fazer seu trabalho enquanto a página permanecer.
Marco Faustinelli

378

.unbind()foi descontinuado e você deve usar o .off()método Basta ligar .off()antes de ligar .on().

Isso removerá todos os manipuladores de eventos:

$(element).off().on('click', function() {
    // function body
});

Para remover apenas os manipuladores de eventos registrados 'clique':

$(element).off('click').on('click', function() {
    // function body
});

9
isso deve ser usado na versão mais recente do jQuery desde unbind, morrer ou viver já é obsoleto
jroi_web

Funciona como um encanto! Minha função seria executada uma vez no clique, depois duas vezes, quatro vezes ... .off () antes de .on () resolvido esse problema.
jumpOnCommand

146

.1()

Uma opção melhor seria .one():

O manipulador é executado no máximo uma vez por elemento por tipo de evento.

$(".bet").one('click',function() {
    //Your function
});

No caso de várias classes e cada classe precisa ser clicada uma vez,

$(".bet").on('click',function() {
    //Your function
    $(this).off('click');   //or $(this).unbind()
});

8
melhor resposta, você me salvou de um truque bobo, isso é muito melhor, porque se você tiver vários onclickeventos para o mesmo elemento, mas em locais diferentes, isso não afetará o resto, unbind()e off()destruirá outros, onclickobrigado novamente
Fanckush

3
depois de tentar todas as respostas, este é o único trabalho para mim.
neversion 26/03/2015

10
Uma coisa a observar, se você deseja que a função seja acionada apenas uma vez por clique, mas continue a ser acionada nos cliques subseqüentes, essa ação literalmente será acionada apenas uma vez durante a vida útil da página. Portanto, a resposta do mtl com o método off (). On () precisará ser usada.
Derek Hewitt

1
@munchschair, pode haver várias razões para isso. Vários elementos com a mesma classe um dentro do outro. Ou um manipulador de cliques é registrado várias vezes em uma iteração.
precisa saber é o seguinte

3
Não está funcionando para mim - ainda é acionado por vários cliques - não faz sentido, pois há apenas 1 elemento de botão com o ID e apenas 1 evento está interceptado (o evento de clique). Eu pensei que este era um bug do jQuery, mas provavelmente é um bug de diálogo modal do Bootstrap.
MC9000 07/11

78

Se você achar que .off () .unbind () ou .stopPropagation () ainda não solucionou seu problema específico, tente usar .stopImmediatePropagation () Funciona muito bem em situações em que você deseja que seu evento seja tratado sem bolhas e sem problemas. efetuando outros eventos que já estão sendo tratados. Algo como:

$(".bet").click(function(event) {
  event.stopImmediatePropagation();
  //Do Stuff
});

faz o truque!


Isso me ajudou tanto quanto impediu que o evento fosse disparado duas vezes, mas também me atrapalhou bastante. Acontece que, se você usar event.stopImmediatePropagation () em um manipulador de eventos anexado a um campo jQuery UI Dialog, por algum motivo, o diálogo não poderá mais fazer referência à instância mais recente de variáveis ​​globais e, em vez disso, usar a versão de variáveis ​​globais antes da renderização da caixa de diálogo jQuery UI. Significado, que, se, por exemplo, a sua variável global foi declarada, mas não inicializado no momento da prestação jQuery UI diálogo, então ele vai aparecer como manipulador de eventos jQuery UI diálogo não inicializada
UkraineTrain

1
ou se você atribuiu a alguma variável global um valor 16 antes de renderizar o jQuery UI Dialog, que mais tarde mudou para, digamos, 55, então 16 aparecerão para essa variável global no manipulador de eventos do jQuery UI Dialog, mesmo que nesse momento seja não mais 16. Portanto, parece um bug da interface do usuário do jQuery que as pessoas devem conhecer.
UcrâniaTreinamento

Para situações muito específicas que são difíceis de descrever. Funciona exatamente como você disse: "Funciona sem afetar nenhum outro evento". Obrigado! :)
Toms Bugna

Funciona completamente bem para mim. Obrigado!
Somnath Pawar

18

Se você está chamando essa função a cada "clique", está adicionando outro par de manipuladores em cada chamada.

Adicionar manipuladores com jQuery não é como definir o valor do atributo "onclick". Pode-se adicionar quantos manipuladores desejar.


4
Sua resposta me colocou na direção certa e aprendi muito, mas não o suficiente para resolver meu problema usando o jQuery, infelizmente. :) Tentei usar on / off, live (e delegar) / die e um e, em seguida, addEventListener / removeEventListener, mas o melhor que pude fazer foi retardar o crescimento exponencial dos manipuladores. Por fim, acabei de resolver meu problema com o onclick. Mas eu ainda aprendi muito sobre o modelo de evento do Javascript, como capturar / borbulhar, com a sua resposta, então eu aprecio isso. Obrigado. :)
Gregory Fowler

Muito obrigado, não acredito que não sabia disso!
Lenny

12

um evento será acionado várias vezes quando for registrado várias vezes (mesmo que para o mesmo manipulador).

por exemplo, $("ctrl").on('click', somefunction)se esse trecho de código for executado toda vez que a página for parcialmente atualizada, o evento também será registrado também. Portanto, mesmo que o ctrl seja clicado apenas uma vez que ele possa executar "alguma função" várias vezes - quantas vezes a execução dependerá de quantas vezes foi registrada.

isso é verdade para qualquer evento registrado em javascript.

solução:

certifique-se de ligar "apenas" uma vez.

e por algum motivo, se você não puder controlar a arquitetura, faça o seguinte:

$("ctrl").off('click'); $("ctrl").on('click', somefunction);


O que você realmente quer dizer?
Somnath Kharat

Isso não explica o que está acontecendo neste caso. O botão tem um ID exclusivo e apenas 1 evento (NÃO está sendo chamado várias vezes, pois pode ser clicado apenas uma vez). Este é um erro que aparece apenas nas caixas de diálogo do jQuery (e é muito fácil de reproduzir).
MC9000 7/16

2
não sabemos se a função "pushBetButtons" é chamada apenas uma vez ou várias vezes .. se for chamada mais de uma vez, o evento será registrado várias vezes e, portanto, também será acionado várias vezes..mesmo se o botão for clicado apenas uma vez .
Kalpesh Popat

4

Eu tive um problema por causa da marcação.

HTML:

<div class="myclass">
 <div class="inner">

  <div class="myclass">
   <a href="#">Click Me</a>
  </div>

 </div>
</div>

jQuery

$('.myclass').on('click', 'a', function(event) { ... } );

Você percebe que eu tenho a mesma classe 'myclass' duas vezes em html, então chama click para cada instância de div.


3

A melhor opção seria usar off

<script>
function flash() {
  $("div").show().fadeOut("slow");
}
$("#bind").click(function() {
  $( "body" )
    .on("click", "#theone", flash)
    .find("#theone")
      .text("Can Click!");
});
$("#unbind").click(function() {
  $("body")
    .off("click", "#theone", flash)
    .find("#theone")
      .text("Does nothing...");
});
</script>

3

Todas as coisas sobre .on () e .one () são ótimas e o jquery é ótimo.

Mas, às vezes, você quer que seja um pouco mais óbvio que o usuário não tem permissão para clicar e, nesses casos, você pode fazer algo assim:

function funName(){
    $("#orderButton").prop("disabled", true);
    //  do a bunch of stuff
    // and now that you're all done
    setTimeout(function(){
        $("#orderButton").prop("disabled",false);
        $("#orderButton").blur();
    }, 3000);
}

e seu botão ficaria assim:

<button onclick='funName()'>Click here</button>

1
Isso resolveria o caso de um usuário realmente clicar várias vezes, mas ... Estou recebendo vários eventos de clique com o mesmo carimbo de data / hora. Não há como clicar três vezes no mesmo milissegundo, mesmo sendo extremamente rápido (e de alguma forma inconsciente de quantas vezes estou pressionando o botão).
jpaugh

2

Isso acontece porque o evento específico é vinculado várias vezes ao mesmo elemento.

A solução que funcionou para mim é:

Mate todos os eventos anexados usando o .die()método

E então anexe seu ouvinte de método.

Portanto,

$('.arrow').click(function() {
// FUNCTION BODY HERE
}

deveria estar:

$('.arrow').die("click")
$('.arrow').click(function() {
// FUNCTION BODY HERE
}

2

Devemos stopPropagation()evitar o evento de gatilhos de cliques muitas vezes.

$(this).find('#cameraImageView').on('click', function(evt) {
   evt.stopPropagation();
   console.log("Camera click event.");
});

Impede que o evento borbulhe na árvore DOM, impedindo que qualquer manipulador pai seja notificado sobre o evento. Este método não aceita argumentos.

Podemos usar event.isPropagationStopped()para determinar se esse método já foi chamado (nesse objeto de evento).

Esse método também funciona para eventos personalizados acionados com trigger (). Observe que isso não impedirá a execução de outros manipuladores no mesmo elemento.


2

No meu caso, eu estava usando 'delegate', então nenhuma dessas soluções funcionou. Acredito que foi o botão exibido várias vezes por meio de chamadas ajax que estava causando o problema de vários cliques. As soluções estavam usando um tempo limite; portanto, apenas o último clique é reconhecido:

var t;
$('body').delegate( '.mybutton', 'click', function(){
    // clear the timeout
    clearTimeout(t);
    // Delay the actionable script by 500ms
    t = setTimeout( function(){
        // do something here
    },500)
})


1
$(element).click(function (e)
{
  if(e.timeStamp !== 0) // This will prevent event triggering more then once
   {
      //do your stuff
   }
}

1

.one é acionado apenas uma vez durante a vida útil da página

Portanto, caso você queira fazer a validação, essa não é a solução certa, porque quando você não sai da página após a validação, nunca mais volta. Melhor usar

$(".bet").on('click',function() 
{ //validation 
   if (validated) { 
      $(".bet").off('click'); //prevent to fire again when we are not yet off the page
      //go somewhere
    }
});

1

Ao lidar com esse problema, sempre uso:

$(".bet").unbind("click").bind("click", function (e) {
  // code goes here
}

Desta forma, eu desdobro e volto no mesmo golpe.


0

https://jsfiddle.net/0vgchj9n/1/

Para garantir que o evento sempre seja acionado apenas uma vez, você pode usar o Jquery .one (). O JQuery one garante que seu manipulador de eventos seja chamado apenas uma vez. Além disso, você pode inscrever seu manipulador de eventos com um para permitir mais cliques quando terminar o processamento da operação de clique atual.

<div id="testDiv">
  <button class="testClass">Test Button</button>
</div>

...

var subscribeClickEvent = function() {$("#testDiv").one("click", ".testClass", clickHandler);};

function clickHandler() {
  //... perform the tasks  
  alert("you clicked the button");
  //... subscribe the click handler again when the processing of current click operation is complete  
  subscribeClickEvent();
}

subscribeClickEvent();

0

Tente assim:

<a href="javascript:void(0)" onclick="this.onclick = false; fireThisFunctionOnlyOnce()"> Fire function </a>

0

No meu caso, o evento onclick foi disparado várias vezes porque eu havia feito um manipulador de eventos genérico comparativamente como

  `$('div').on("click", 'a[data-toggle="tab"]',function () {
        console.log("dynamic bootstrap tab clicked");
        var href = $(this).attr('href');
        window.location.hash = href;
   });`

alterado para

    `$('div#mainData').on("click", 'a[data-toggle="tab"]',function () {
        console.log("dynamic bootstrap tab clicked");
        var href = $(this).attr('href');
        window.location.hash = href;
    });`

e também precisa criar manipuladores separados para cliques estáticos e dinâmicos, para cliques estáticos na guia

    `$('a[data-toggle="tab"]').on("click",function () {
        console.log("static bootstrap tab clicked");
        var href = $(this).attr('href');
        window.location.hash = href;
    });`

0

No meu caso, eu carreguei o mesmo *.jsarquivo na página duas vezes em uma <script>tag, portanto os dois arquivos estavam anexando manipuladores de eventos ao elemento. Eu removi a declaração duplicada e isso corrigiu o problema.


0

Outra solução que encontrei foi essa, se você tiver várias classes e estiver lidando com botões de opção enquanto clica no rótulo.

$('.btn').on('click', function(e) {
    e.preventDefault();

    // Hack - Stop Double click on Radio Buttons
    if (e.target.tagName != 'INPUT') {
        // Not a input, check to see if we have a radio
        $(this).find('input').attr('checked', 'checked').change();
    }
});

0

Eu estava tendo esse problema com um link gerado dinamicamente:

$(document).on('click', '#mylink', function({...do stuff...});

Eu encontrei a substituição documentpor 'body'corrigido o problema para mim:

$('body').on('click', '#mylink', function({...do stuff...});


0

Unbind () funciona, mas isso pode causar outros problemas no futuro. O manipulador é acionado várias vezes quando está dentro de outro manipulador; portanto, mantenha seu manipulador fora e, se desejar os valores do manipulador aninhado, atribua-os a uma variável global que será acessível ao seu manipulador.



-1

O código abaixo funcionou para mim no meu aplicativo de bate-papo para lidar com vários eventos de acionamento de clique do mouse mais de uma vez. if (!e.originalEvent.detail || e.originalEvent.detail == 1) { // Your code logic }

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.