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

Gregory Fowler
fonte
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
});
Roubar
fonte
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
});
mtl
fonte
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()
});
Shaunak D
fonte
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!

Alfonse Pinto
fonte
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.

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

Kalpesh Popat
fonte
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.

Bobz
fonte
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>
arc_shiva
fonte
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>
rikkitikkitumbo
fonte
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
}
Aluno
fonte
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.

Daniel Raja Singh
fonte
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)
})
Trevor Lettman
fonte
1
$(element).click(function (e)
{
  if(e.timeStamp !== 0) // This will prevent event triggering more then once
   {
      //do your stuff
   }
}
Rajan Rajan M
fonte
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
    }
});
Pieter van Kampen
fonte
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.

Andy
fonte
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();
Razan Paul
fonte
0

Tente assim:

<a href="javascript:void(0)" onclick="this.onclick = false; fireThisFunctionOnlyOnce()"> Fire function </a>
Adam Kozlowski
fonte
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;
    });`
Awais Nasir
fonte
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.

inostia
fonte
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();
    }
});
Andrew Vink
fonte
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...});

nateM
fonte
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.

praveen poonja
fonte
0

Caso isso funcione bem

$( "#ok" ).bind( "click", function() {
    console.log("click"); 
});
santosh vishwakarma
fonte
-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 }

user2792303
fonte