Les événements jQuery click se déclenchent plusieurs fois

284

J'essaie d'écrire un jeu de vidéo poker en Javascript comme un moyen d'en descendre les bases, et j'ai rencontré un problème où les gestionnaires d'événements click jQuery se déclenchent plusieurs fois.

Ils sont attachés aux boutons pour placer un pari, et cela fonctionne très bien pour placer un pari sur la première main pendant un jeu (tirer une seule fois); mais en pariant pour la seconde main, il déclenche l'événement de clic deux fois à chaque fois qu'un bouton de pari ou de pari est pressé (donc le montant correct est parié deux fois pour chaque pression). Dans l'ensemble, il suit ce modèle pour le nombre de fois que l'événement de clic est déclenché lorsque vous appuyez une fois sur un bouton de pari - où le ième terme de la séquence est pour le pari de la ième main depuis le début du jeu: 1, 2, 4 , 7, 11, 16, 22, 29, 37, 46, ce qui semble être n (n + 1) / 2 + 1 pour tout ce qui vaut - et je n'étais pas assez intelligent pour le comprendre, j'ai utilisé OEIS . :)

Voici la fonction avec les gestionnaires d'événements click qui agissent; j'espère que c'est facile à comprendre (faites le moi savoir si non, je veux aussi m'améliorer):

/** 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
        }
    });
}

S'il vous plaît laissez-moi savoir si vous avez des idées ou des suggestions, ou si la solution à mon problème est similaire à une solution à un autre problème ici (j'ai regardé de nombreux sujets de même titre et je n'ai pas eu de chance de trouver une solution qui pourrait fonctionner pour moi).

Gregory Fowler
la source
var amount = parseInt(this.id.replace(/[^\d]/g,''),10);Et si vous comptez utiliser plusieurs fois la même propriété d'un élément , mettez-la en cache . La recherche coûte cher.
David dit de réintégrer Monica le
Merci pour la réponse et le conseil sur les propriétés de mise en cache. Je mets player.money et player.bet sur des variables locales argent et parie à l'intérieur de cette fonction et les manipule à la place, et je changerai le reste de mon code pour le faire aussi. :) Si vous avez le temps, pourriez-vous également expliquer ce que vous avez suggéré l'initialisation du montant fait; cela ressemble à une expression régulière, mais je ne peux pas le comprendre facilement.
Gregory Fowler
@GregoryFowler - sans rapport avec votre question, mais ... une déclaration de commutateur javascript pourrait être utile d'examiner.
Clayton
2
Man, votre fonction place un gestionnaire de clic chaque fois qu'il est invoqué. Si vous l'invoquez à chaque tour, au deuxième tour, vous avez deux gestionnaires et ainsi de suite. Chaque gestionnaire fait son travail et au tour 100, vous obtenez 100 alertes.
Marco Faustinelli
Cela se produit car quelque part dans votre code, vous reliez le gestionnaire d'événements sans le délier au préalable. Voir cette question pour un scénario similaire et une bonne explication.
jpaugh

Réponses:

528

Pour vous assurer qu'une action de clic uniquement une fois, utilisez ceci:

$(".bet").unbind().click(function() {
    //Stuff
});
Rob
la source
31
OK, où étais-tu hier, Rob? ;) C'est exactement ce que je cherchais, je ne sais pas pourquoi je ne l'ai pas trouvé avant. Mais c'était quand même une bénédiction déguisée car j'ai appris beaucoup d'autres choses.
Gregory Fowler
1
;) Désolé. J'ai fait tourner mes roues pendant quelques jours sur celui-ci aussi. Je cherche toujours une raison logique pour expliquer pourquoi cela se produit même en premier lieu.
Rob
6
L'homme après tant de choses, cela l'a fait. Sauf dans mon cas, j'ai utilisé l'équivalent de: $ (". Bet"). Off (). On ()
MadTurki
7
Comme le dit jroi_web ci-dessous, cette méthode est déconseillée. Voir la réponse de @ trolle pour une solution plus récente.
Pascal
Un gestionnaire de clics n'est pas une chose unique à jeter. Surtout quand c'est le si-si-si volumineux montré dans la question. Vous êtes censé le joindre et le laisser faire son travail aussi longtemps que la page existe.
Marco Faustinelli
380

.unbind()est obsolète et vous devez utiliser la .off()méthode à la place. Appelez simplement .off()juste avant d'appeler .on().

Cela supprimera tous les gestionnaires d'événements:

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

Pour supprimer uniquement les gestionnaires d'événements «clic» enregistrés:

$(element).off('click').on('click', function() {
    // function body
});
mtl
la source
9
cela devrait être utilisé dans la nouvelle version de jQuery car dissocier, mourir ou vivre est déjà déconseillé
jroi_web
Fonctionne comme un charme! Ma fonction s'exécutait une fois au clic, puis deux fois, quatre fois ... .off () avant que .on () ne résout ce problème.
jumpOnCommand
146

.une()

Une meilleure option serait .one():

Le gestionnaire est exécuté au plus une fois par élément par type d'événement.

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

Dans le cas de plusieurs classes et chaque classe doit être cliquée une fois,

$(".bet").on('click',function() {
    //Your function
    $(this).off('click');   //or $(this).unbind()
});
Shaunak D
la source
8
meilleure réponse, vous m'a sauvé d'un hack stupide, ce qui est beaucoup mieux parce que si vous avez plusieurs onclickévénements au même élément , mais dans différents endroits cela n'affectera le reste, alors que unbind()et off()va simplement détruire d' autres onclicks vous remercie encore une fois
Fanckush
3
après avoir essayé toutes les réponses, celui-ci est le seul travail pour moi.
neversion
10
Une chose à noter, si vous voulez que la fonction ne se déclenche qu'une seule fois PAR CLIC, mais continue à se déclencher sur les clics suivants, celle-ci ne se déclenchera littéralement qu'une seule fois pour la durée de vie de la page. La réponse de mtl avec la méthode off (). On () devra donc être utilisée.
Derek Hewitt
1
@munchschair, il peut y avoir plusieurs raisons à cela. Plusieurs éléments avec la même classe les uns dans les autres. Ou un gestionnaire de clics est enregistré plusieurs fois dans une itération.
Shaunak D
3
Ne pas fonctionner pour moi - toujours plusieurs clics - n'a aucun sens car il n'y a qu'un seul élément de bouton avec l'ID et un seul événement est piégé (l'événement de clic). Je pensais que c'était un bogue jQuery, mais c'est probablement un bogue de boîte de dialogue modale Bootstrap.
MC9000
78

Si vous trouvez que .off () .unbind () ou .stopPropagation () ne résout toujours pas votre problème spécifique, essayez d'utiliser .stopImmediatePropagation () Fonctionne très bien dans les situations où vous voulez juste que votre événement soit géré sans bouillonnement et sans effectuer tous les autres événements déjà traités. Quelque chose comme:

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

fait l'affaire!

Alfonse Pinto
la source
Cela m'a aidé à empêcher l'événement de se déclencher deux fois, mais cela m'a aussi beaucoup gâché. Il s'avère que si vous utilisez event.stopImmediatePropagation () dans un gestionnaire d'événements attaché à un champ de dialogue jQuery UI, alors, pour une raison quelconque, la boîte de dialogue ne peut plus référencer la dernière instance de variables globales et utilise à la place la version des variables globales avant le rendu de la boîte de dialogue jQuery UI. Cela signifie que si, par exemple, votre variable globale a été déclarée, mais qu'elle n'a pas été initialisée au moment du rendu de la boîte de dialogue d'interface utilisateur jQuery, elle apparaîtra comme un gestionnaire d'événements de boîte de dialogue jQuery UI non initialisé
UkraineTrain
1
ou si vous avez attribué à une variable globale une valeur de 16 avant le rendu de jQuery UI Dialog, qui a ensuite changé pour, disons, 55, alors 16 s'affichera pour cette variable globale dans le gestionnaire d'événements jQuery UI Dialog, même si à ce moment plus 16. Ainsi, il ressemble à un bogue jQuery UI que les gens devraient connaître.
UkraineTrain
Pour des situations très spécifiques qui sont même difficiles à décrire. Cela fonctionne exactement comme Vous l'avez dit: "Fonctionne sans effectuer d'autres événements". Merci! :)
Toms Bugna
Fonctionne très bien pour moi. Merci!
Somnath Pawar
18

Si vous appelez cette fonction à chaque "clic", cela ajoute une autre paire de gestionnaires à chaque appel.

Ajouter des gestionnaires avec jQuery n'est pas comme définir la valeur de l'attribut "onclick". On peut ajouter autant de gestionnaires que l'on désire.

Pointu
la source
4
Votre réponse m'a mis dans la bonne direction et j'ai beaucoup appris, mais pas assez pour résoudre mon problème en utilisant malheureusement jQuery. :) J'ai essayé d'utiliser on / off, live (et délégué) / die, et un, puis addEventListener / removeEventListener, mais le mieux que j'ai pu faire était de ralentir la croissance exponentielle des gestionnaires. J'ai finalement résolu mon problème avec onclick pour le moment. Mais j'ai encore beaucoup appris sur le modèle d'événement de Javascript, comme la capture / propagation, de votre réponse, donc je l'apprécie. Je vous remercie. :)
Gregory Fowler
Merci beaucoup, je ne peux pas croire que je ne savais pas ça!
Lenny
12

un événement se déclenche plusieurs fois lorsqu'il est enregistré plusieurs fois (même s'il concerne le même gestionnaire).

Par exemple, $("ctrl").on('click', somefunction)si ce morceau de code est exécuté à chaque fois que la page est partiellement rafraîchie, l'événement est également enregistré à chaque fois. Par conséquent, même si le bouton Ctrl n'est cliqué qu'une seule fois, il peut exécuter plusieurs fois "une fonction" - le nombre de fois qu'il dépendra du nombre de fois qu'il a été enregistré.

cela est vrai pour tout événement enregistré en javascript.

Solution:

assurez-vous d'appeler "on" une seule fois.

et pour une raison quelconque, si vous ne pouvez pas contrôler l'architecture, procédez comme suit:

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

Kalpesh Popat
la source
Qu'est-ce que tu veux vraiment dire?
Somnath Kharat
Cela n'explique pas ce qui se passe dans ce cas. Le bouton a un ID unique et un seul événement (il N'EST PAS appelé plusieurs fois, car il ne peut être cliqué qu'une seule fois). Il s'agit d'un bogue qui n'apparaît que dans les boîtes de dialogue jQuery (et très facile à reproduire).
MC9000
2
nous ne savons pas si la fonction "pushingBetButtons" est appelée une seule fois ou plusieurs fois .. si elle est appelée plus d'une fois, l'événement est enregistré plusieurs fois et donc il se déclenchera également plusieurs fois .. même si le bouton n'est cliqué qu'une seule fois .
Kalpesh Popat
4

J'ai eu un problème à cause du balisage.

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) { ... } );

Vous remarquez que j'ai la même classe 'myclass' deux fois en html, donc il appelle click pour chaque instance de div.

Bobz
la source
3

La meilleure option serait d'utiliser 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
la source
3

Tout ce qui concerne .on () et .one () est génial, et jquery est génial.

Mais parfois, vous voulez qu'il soit un peu plus évident que l'utilisateur n'est pas autorisé à cliquer, et dans ces cas, vous pouvez faire quelque chose comme ceci:

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

et votre bouton ressemblerait à:

<button onclick='funName()'>Click here</button>
rikkitikkitumbo
la source
1
Cela résoudrait le cas d'un utilisateur cliquant en fait plusieurs fois, mais ... j'obtiens plusieurs événements de clic avec le même horodatage. Il n'y a aucun moyen que je puisse cliquer 3 fois dans la même milliseconde, même si j'étais extrêmement rapide (et je ne sais pas combien de fois j'appuie sur le bouton).
jpaugh
2

Cela se produit car l'événement particulier est lié plusieurs fois au même élément.

La solution qui a fonctionné pour moi est:

Tuez tous les événements attachés en utilisant la .die()méthode.

Et puis attachez votre écouteur de méthode.

Donc,

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

devrait être:

$('.arrow').die("click")
$('.arrow').click(function() {
// FUNCTION BODY HERE
}
Élève
la source
2

Nous devons stopPropagation()Afin d'éviter les clics déclencheurs trop d'événements.

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

Il empêche l'événement de se propager dans l'arborescence DOM, empêchant les gestionnaires parents d'être notifiés de l'événement. Cette méthode n'accepte aucun argument.

Nous pouvons utiliser event.isPropagationStopped()pour déterminer si cette méthode a déjà été appelée (sur cet objet événement).

Cette méthode fonctionne également pour les événements personnalisés déclenchés avec trigger (). Notez que cela n'empêchera pas les autres gestionnaires du même élément de s'exécuter.

Daniel Raja Singh
la source
2

Dans mon cas, j'utilisais «délégué», donc aucune de ces solutions n'a fonctionné. Je pense que c'est le bouton apparaissant plusieurs fois via les appels ajax qui a causé le problème des clics multiples. Les solutions utilisaient un délai d'attente, donc seul le dernier clic est reconnu:

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
la source
1
$(element).click(function (e)
{
  if(e.timeStamp !== 0) // This will prevent event triggering more then once
   {
      //do your stuff
   }
}
Rajan Rajan M
la source
1

.un ne se déclenche qu'une seule fois pendant la durée de vie de la page

Donc, si vous voulez faire une validation, ce n'est pas la bonne solution, car lorsque vous ne quittez pas la page après la validation, vous ne revenez jamais. Mieux utiliser

$(".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
la source
1

Lorsque je traite ce problème, j'utilise toujours:

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

De cette façon, je délie et ressaisis du même coup.

Andy
la source
0

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

Pour vous assurer que l'événement ne se déclenche toujours qu'une seule fois, vous pouvez utiliser Jquery .one (). JQuery one garantit que votre gestionnaire d'événements n'a appelé qu'une seule fois. En outre, vous pouvez souscrire votre gestionnaire d'événements avec un pour autoriser d'autres clics lorsque vous avez terminé le traitement de l'opération de clic en cours.

<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
la source
0

Essayez de cette façon:

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

Dans mon cas, l'événement onclick se déclenchait plusieurs fois car j'avais fait un gestionnaire d'événements générique

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

changé en

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

et doivent également créer des gestionnaires distincts pour les clics statiques et dynamiques, pour les clics statiques sur les onglets

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

Dans mon cas, j'avais chargé le même *.jsfichier sur la page deux fois dans une <script>balise, donc les deux fichiers attachaient des gestionnaires d'événements à l'élément. J'ai supprimé la déclaration en double et cela a résolu le problème.

inostia
la source
0

Une autre solution que j'ai trouvée était la suivante, si vous avez plusieurs classes et que vous traitez des boutons radio en cliquant sur l'étiquette.

$('.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
la source
0

J'avais ce problème avec un lien généré dynamiquement:

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

J'ai trouvé le remplacement documentpar 'body'résolu le problème pour moi:

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

nateM
la source
0

Unbind () fonctionne, mais cela pourrait entraîner d'autres problèmes à l'avenir. Le gestionnaire se déclenche plusieurs fois lorsqu'il se trouve à l'intérieur d'un autre gestionnaire, alors laissez votre gestionnaire à l'extérieur et si vous voulez les valeurs du gestionnaire qui avaient été imbriquées, affectez-les à une variable globale qui sera accessible au gestionnaire.

praveen poonja
la source
0

Au cas où cela fonctionnerait bien

$( "#ok" ).bind( "click", function() {
    console.log("click"); 
});
Santosh vishwakarma
la source
-1

Le code ci-dessous a fonctionné pour moi dans mon application de chat pour gérer plusieurs événements déclenchant un clic de souris plus d'une fois. if (!e.originalEvent.detail || e.originalEvent.detail == 1) { // Your code logic }

user2792303
la source