348

I'm attempting to write a video poker game in Javascript as a way of getting the basics of it down, and I've run into a problem where the jQuery click event handlers are firing multiple times.

They're attached to buttons for placing a bet, and it works fine for placing a bet on the first hand during a game (firing only once); but in betting for the second hand, it fires the click event twice each time a bet or place bet button is pressed (so twice the correct amount is bet for each press). Overall, it follows this pattern for number of times the click event is fired when pressing a bet button once--where the ith term of the sequence is for the betting of the ith hand from the beginning of the game: 1, 2, 4, 7, 11, 16, 22, 29, 37, 46, which appears to be n(n+1)/2 + 1 for whatever that's worth--and I wasn't smart enough to figure that out, I used OEIS. :)

Here's the function with the click event handlers that are acting up; hopefully it's easy to understand (let me know if not, I want to get better at that as well):

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

Please let me know if you have any ideas or suggestions, or if the solution to my problem is similar to a solution to another problem on here (I've looked at many similarly titled threads and had no luck in finding a solution that could work for me).

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Gregory Fowler
  • 3,623
  • 2
  • 13
  • 7
  • `var amount = parseInt(this.id.replace(/[^\d]/g,''),10);` And if you're going to use the same property of an element more than once *cache* that property, don't keep looking it up. The look-up is expensive. – David Thomas Feb 20 '13 at 00:05
  • Thank you for the response, and the tip on caching properties. I set player.money and player.bet to local variables money and bet inside that function and manipulated those instead, and will change the rest of my code to do that also.:) If you have time, could you also explain what your suggested initialization of amount is doing; it looks like some regular expression, but I can't make sense of it easily. – Gregory Fowler Feb 20 '13 at 20:55
  • @GregoryFowler - unrelated to your question, but... a javascript switch statement might be worth looking into. – Clayton Jun 05 '14 at 02:37
  • 2
    Man, your function is placing a click handler each time it is invoked. If you invoke it at each round, at round two you have two handlers and so on. Each handler does its job and at round 100 you get 100 alerts. – Marco Faustinelli Jun 19 '15 at 05:38
  • This happens because somewhere in your code, you're rebinding the event handler without first unbinding it. See this question for a [similar scenario](https://stackoverflow.com/q/20733638/712526), and a good explanation. – jpaugh Oct 23 '17 at 23:01
  • Possible duplicate of [click event fires multiple times issue, how to?](https://stackoverflow.com/questions/20733638/click-event-fires-multiple-times-issue-how-to) – jpaugh Oct 23 '17 at 23:01

29 Answers29

631

To make sure a click only actions once use this:

$(".bet").unbind().click(function() {
    //Stuff
});
robsch
  • 9,358
  • 9
  • 63
  • 104
Rob
  • 11,185
  • 10
  • 36
  • 54
  • 39
    OK, where were you yesterday, Rob? ;) That's exactly what I was looking for, don't know why I didn't find it before. But it was still a blessing in disguise because I did learn a lot of other stuff. – Gregory Fowler Feb 21 '13 at 03:49
  • 2
    ;) sorry. I spun my wheels around for a few days on this one too. I am still searching for a logical reason as to why it even happens in the first place. – Rob Feb 21 '13 at 04:11
  • 6
    Man after so many things this did it. Except in my case I used the equivalent of: $(".bet").off().on() – MadTurki Oct 24 '13 at 03:30
  • 8
    As jroi_web says below, this method is deprecated. See @trolle's answer for a more recent solution. – Pascal Sep 11 '14 at 06:56
  • 1
    A click handler ain't a one-off throwaway thing. Especially when it's the bulky if-if-if shown in the question. You're supposed to attach it and let it do its work as long as the page lives. – Marco Faustinelli Jun 19 '15 at 05:44
  • 1
    Although this is the most upvoted as of today, using [.one()](http://api.jquery.com/one/) seems to be more correct. See the [answer](http://stackoverflow.com/a/24218474/1159167) below. – Ricardo May 12 '16 at 18:58
  • Why does this even happen? I have plenty of click functions that don't fire off multiple times. – munchschair Aug 27 '16 at 02:36
  • This solved an issue for me in MVC where I had a button that I wanted to be capable of clicking multiple times and fire a function. Something prevented it from happening. Is it something in jquery, bootstrap, or a different default library that prevents double/multiple clicks? – Trevor Nestman Dec 02 '16 at 18:07
  • It's hidding the problem but not solving it! In my case `.on()` accidentally stucked inside `forEach()` loop – vladkras Mar 10 '17 at 16:33
  • great answer took me almost a whole day debugging my code but this answer was clear. – qualebs May 29 '17 at 22:04
  • this saved my asian ass so hard i was wondering why my data sent like 10 times now i need to clear my messed up database thanks so upvoted – Munkhdelger Tumenbayar Aug 31 '17 at 07:33
  • This maybe almost 6 years ago. But this worked after a couple of hours searching. After we had a ajax call filling a form. Clicks where trigged multiple times. $('body').unbind().on('click', 'classname', function (){}); did the trick for us. Thanks ! – NME New Media Entertainment Nov 05 '19 at 01:44
  • @Rob thanks, it worked for me but can't understand this behavior could you please help - when button click event used with jquery datatable. the number of times click event occurs is equals to the number of times i am creating the datatable in ajax success without reloading the page. – Muhammad Tarique Aug 11 '20 at 12:17
  • This does resolve the issue, but I have many click events and this is the first time I am having an issue. Can you please explain why this happens? Is it recommend to always remove/unbind the event handler? – Ben Dec 07 '22 at 17:28
  • ... and you keep saving lives in 2022 with this answer! – JS_LnMstr Dec 15 '22 at 11:02
456

.unbind() is deprecated and you should use the .off() method instead. Simply call .off() right before you call .on().

This will remove all event handlers:

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

To only remove registered 'click' event handlers:

$(element).off('click').on('click', function() {
    // function body
});
mtl
  • 7,529
  • 2
  • 20
  • 22
  • 12
    this should be used in the newer version of jQuery since unbind, die or live is already deprecated – jroi_web Aug 20 '14 at 06:33
  • This does resolve the issue, but I have many click events and this is the first time I am having an issue. Can you please explain why this happens? Is it recommend to always remove the event handler? – Ben Dec 07 '22 at 17:27
174

.one()

A better option would be .one() :

The handler is executed at most once per element per event type.

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

In case of multiple classes and each class needs to be clicked once,

$(".bet").on('click',function() {
    //Your function
    $(this).off('click');   //or $(this).unbind()
});
Shaunak D
  • 20,588
  • 10
  • 46
  • 79
  • 10
    best answer, you saved me from a silly hack, this is much better because it if you have multiple `onclick` events to the same element but in different locations this will not affect the rest, while `unbind()` and `off()` will just destroy other `onclick`s thank you again – Fanckush Sep 30 '14 at 00:39
  • 3
    after trying all answers, this one is the only one work for me. – neversion Mar 26 '15 at 00:46
  • 15
    One thing to note, if you want the function to only fire once PER CLICK, but continue to fire on subsequent clicks, this one will literally only fire once for the life of the page. So mtl's answer with the off().on() method will need to be used. – Derek Hewitt Feb 23 '16 at 19:19
  • This worked for me, but why? Why do some .on('clic') functions only fire once and others fire multiple times? This is the only time I've found myself needing this function. – munchschair Aug 27 '16 at 02:44
  • 1
    @munchschair, there can be multiple reasons for this. Multiple elements with same class inside each other. Or a click handler is registered multiple time in an iteration. – Shaunak D Aug 29 '16 at 08:47
  • Any reason for the latter to happen if the user only pressed the button once? – munchschair Aug 29 '16 at 14:04
  • 3
    Not working for me - still multiple click firings - makes no sense as there's only 1 button element with the ID and only 1 event is trapped (the click event). I thought this was a jQuery bug, but it's probably a Bootstrap modal-dialog bug. – MC9000 Nov 07 '16 at 10:02
  • Not working for me either in some cases. Could not narrow down the reason why though... I was clicking on a button `one('click')` inside a modal with `on('clic')`. – Yohan Obadia Jan 28 '19 at 17:10
  • 1
    I kind of feel like this is a hack (not the answer, the answer is sound) but needing to use one() in the first place. This worked for me but I feel like I need to understand why I have to use this instead of fixing the underlying issue (which I can't figure out). But thank you for the solution! – HPWD Jul 03 '19 at 20:47
117

If you find that .off() .unbind() or .stopPropagation() still doesn't fix your specific issue, try using .stopImmediatePropagation() Works great in situations when you just want your event to be handled without any bubbling and without effecting any other events already being handled. Something like:

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

does the trick!

Alfonse Pinto
  • 1,371
  • 1
  • 9
  • 9
  • This helped me as well as far as stopping event from firing twice, but also messed me up big time. It turns out that if you use event.stopImmediatePropagation() in an event handler attached to a jQuery UI Dialog field, then, for some reason, the dialog can no longer reference the newest instance of global variables and instead uses the version of global variables prior to jQuery UI Dialog rendering. Meaning, that if, for example, your global variable was declared, but not initialized at the time of jQuery UI Dialog rendering, then it will show up as uninitialized jQuery UI Dialog event handler – UkraineTrain Aug 03 '15 at 23:23
  • 2
    or if you assigned to some global variable a value of 16 prior to rendering jQuery UI Dialog, which later changed to, say, 55, then 16 will show up for that global variable in jQuery UI Dialog event handler, even if at that time it's no longer 16. So, it looks like a jQuery UI bug that people should be aware of. – UkraineTrain Aug 03 '15 at 23:27
  • 1
    For very specific situations that are even difficult to describe. It works exactly as You said: "Works without effecting any other events". Thank You! :) – Toms Bugna May 18 '16 at 11:45
  • 2
    I went to more than 15 answers, finally found this `e.stopImmediatePropagation`! – WEshruth Oct 08 '20 at 05:08
  • I was only looking for `.stopPropagation()` but still this answer gave a quick overview of methods to consider! – Bence Szalai Sep 04 '22 at 09:23
20

an Event will fire multiple time when it is registered multiple times (even if to the same handler).

eg $("ctrl").on('click', somefunction) if this piece of code is executed every time the page is partially refreshed, the event is being registered each time too. Hence even if the ctrl is clicked only once it may execute "somefunction" multiple times - how many times it execute will depend on how many times it was registered.

this is true for any event registered in javascript.

solution:

ensure to call "on" only once.

and for some reason if you cannot control the architecture then do this:

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

Kalpesh Popat
  • 1,416
  • 14
  • 12
  • What You really want to say? – Somnath Kharat Apr 08 '15 at 13:35
  • That does not explain what is going on in this case. The button has one unique ID and only 1 event (it's NOT being called multiple times, since it can only be clicked once). This is a bug that only appears in jQuery dialog boxes (and very easy to reproduce). – MC9000 Nov 07 '16 at 09:58
  • 3
    we do not know whether the function "pushingBetButtons" is called only once or multiple times.. if it is called more than once, than the event is registered multiple times and hence it will also fire multipletimes..even if button is clicked just once. – Kalpesh Popat Jan 04 '17 at 12:04
18

If you're calling that function on each "click", then it's adding another pair of handlers on each call.

Adding handlers with jQuery just isn't like setting the value of the "onclick" attribute. One can add as many handlers as one desires.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 4
    Your answer set me in the right direction and I learned a lot, but not enough to solve my problem using jQuery unfortunately. :) I tried using on/off, live (and delegate)/die, and one, and then addEventListener/removeEventListener, but the best I could do was slow the exponential growth of handlers. I eventually just solved my problem with onclick for the time being. But I still learned a lot about Javascript's event model, like capturing/bubbling, from your answer, so I appreciate it. Thank you. :) – Gregory Fowler Feb 20 '13 at 20:49
11
$('.bed').one(function(){ })

Docs:

http://api.jquery.com/one/

double-beep
  • 5,031
  • 17
  • 33
  • 41
Conner Gesbocker
  • 384
  • 3
  • 12
6

We must to stopPropagation() In order to avoid Clicks triggers event too many times.

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

It Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event. This method does not accept any arguments.

We can use event.isPropagationStopped() to determine if this method was ever called (on that event object).

This method works for custom events triggered with trigger(), as well.Note that this will not prevent other handlers on the same element from running.

vimal1083
  • 8,499
  • 6
  • 34
  • 50
4

I had a problem because of markup.

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

You notice I have the same class 'myclass' twice in html, so it calls click for each instance of div.

Bobz
  • 2,345
  • 2
  • 18
  • 22
4

When I deal with this issue, I always use:

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

This way I unbind and rebind in the same stroke.

Andy
  • 49
  • 6
3

The better option would be using 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

All the stuff about .on() and .one() is great, and jquery is great.

But sometimes, you want it to be a little more obvious that the user isn't allowed to click, and in those cases you could do something like this:

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

and your button would look like:

<button onclick='funName()'>Click here</button>
rikkitikkitumbo
  • 954
  • 3
  • 17
  • 38
  • 1
    That would solve the case of a user actually clicking multiple times, but... I'm getting multiple click events with the same timestamp. There's no way I could click 3 times in the same millisecond, even if I were *extremely* quick at it (and somehow unaware of how many times I'm pressing the button). – jpaugh Oct 23 '17 at 22:46
2

It happens due to the particular event is bound multiple times to the same element.

The solution which worked for me is:

Kill all the events attached using .die() method.

And then attach your method listener.

Thus,

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

should be:

$('.arrow').die("click")
$('.arrow').click(function() {
// FUNCTION BODY HERE
}
Pupil
  • 23,834
  • 6
  • 44
  • 66
2

In my case I was using 'delegate', so none of these solutions worked. I believe it was the button appearing multiple times via ajax calls that was causing the multiple click issue. The solutions was using a timeout so only the last click is recognized:

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

I was having this problem with a dynamically generated link:

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

I found replacing document with 'body' fixed the issue for me:

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

nateM
  • 502
  • 4
  • 10
  • 25
  • Wow, thanks for posting this. Was desperately looking for why $(document).on was firing multiple times! Why does using 'body' work instead? This must be a bug in JQuery. – nwood21 Jul 13 '22 at 00:26
1
$(element).click(function (e)
{
  if(e.timeStamp !== 0) // This will prevent event triggering more then once
   {
      //do your stuff
   }
}
Rajan Rajan M
  • 65
  • 1
  • 3
1

.one only fires once for the lifetime of the page

So in case you want to do validation, this is not the right solution, because when you do not leave the page after validation, you never come back. Better to use

$(".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
  • 1,957
  • 17
  • 21
0

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

To make sure the event always only fires once, you can use Jquery .one() . JQuery one ensures that your event handler only called once. Additionally, you can subscribe your event handler with one to allow further clicks when you have finished the processing of the current click operation.

<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
  • 13,618
  • 3
  • 69
  • 61
0

Try that way:

<a href="javascript:void(0)" onclick="this.onclick = false; fireThisFunctionOnlyOnce()"> Fire function </a>
Adam Kozlowski
  • 5,606
  • 2
  • 32
  • 51
0

In my case, onclick event was firing multiple times coz I had made a generic event handler comparatively as

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

changed to

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

and also have to make separate handlers for static and dynamic clicks, for static tab click

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

In my case I had loaded the same *.js file on the page twice in a <script> tag, so both files were attaching event handlers to the element. I removed the duplicate declaration and that fixed the problem.

inostia
  • 7,777
  • 3
  • 30
  • 33
0

Another solution I found was this, if you have multiple classes and are dealing with radio buttons while clicking on the label.

$('.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
  • 321
  • 1
  • 4
  • 7
0

In case this works fine

$( "#ok" ).bind( "click", function() {
    console.log("click"); 
});
0

Reference @Pointy answer. if this in loop, one can avoid click events firing multiple times by doing something like this:

 $(".bet").each(function(i,item){
       $(this).on({
            click:function(){
                //TRIGGERS ONES
            if(i){
              console.log(i); 
                //DO YOUR THING HERE
            }
            
            }
        });

});
ShapCyber
  • 3,382
  • 2
  • 21
  • 27
0

use Event.stopPropagation(). It will work.

Dan
  • 140
  • 1
  • 6
0

This solution worked for me. When your element added dynamic, you need to do it this way to achieve it. This answer may be for future checking user.

$("body").off("click", ".Element").on("click", ".Element", function(e){
     e.preventDefault();
    //Your code here 
}); 
Kvvaradha
  • 732
  • 1
  • 13
  • 28
0

If you just write this:

$(document).off('click').on("click", "#btnID", function(){ })

Then, it will disable click event of all items.

If you just want to off click the specific button, do this

$(document).off('click', "#btnID").on("click", "#btnID", function(){ })

Nabin
  • 163
  • 1
  • 12
-1

The below code worked for me in my chat application to handle multiple mouse click triggering events more than once. if (!e.originalEvent.detail || e.originalEvent.detail == 1) { // Your code logic }

-1

Unbind () works, but that might cause other issues in the future. The handler triggers multiple times when it is inside another handler, so keep your handler outside and if you want the values of the handler which had nested, assign them to a global variable that it will be accessible to the your handler.