How to prevent calling of en event handler twice on fast clicks?
Asked Answered
K

10

23

There is a button and when user clicks on button, some data is saved to back-end. Issue is when user clicks on button very quickly, event handler is getting executed multiple times.

This is the code

var x = 1;
$('#button').click(function() {
  // Do something
  // Save some data on network
  x++;
  console.log(x);
});

I want this handler to get executed when user clicks on button just once. Even In case of double or tripple click, this should get executed only once. I just want to avoid quick clicks, this handler can get executed again ofcourse

I have multiple solutions in my mind like

  1. Define a global variable like IS_BUTTON_HANDLER_WORKING = false and when you enter the handler set it to true and in the end set it to false again. And check if it is true just return from the function.

  2. Detach the handler in the beginning and reattach in the end.

Consider you have 25 buttons in your application. What should be the best approach to implement this.

Take a look at this Fiddle

Solution

$('#button').click(function() {
   $(this).attr('disabled', true);
  // Do something
  // Save some data on network
  $(this).removeAttr('disabled');
});

Using this, we are sure that our next handler will get executed only when previous execution has been done completely.

Kenley answered 29/11, 2013 at 8:46 Comment(2)
Try using one('click', fn) instead of clickGondola
I don't think one is the right answer because the handler won't get executed again. You may want to submit the form with updated data again after some time.Kenley
S
19

David Walsh has a great solution.

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};
Stacy answered 21/11, 2016 at 19:44 Comment(4)
Yes I read that post a lot later after I posted this. I am changing the accepted answer to this since this is the correct approach. Thanks for bringing it here!!Kenley
Seems like you need throttle, not debounce if you want event to trigger immediately and allow another call only after certain time.Celebration
@Celebration can you give an example of the throttle? I think it's what I'm looking forBegin
Shalom @RamiAlloush, try thisStacy
P
11

There are multiple ways of dealing with this:

You can disable/hide the button after the click:

$('#button').attr("disabled", true);

You can also set a timeout on each click to ensure it does not execute again:

var x, y = 1;

$('#button').click(function() {
  if (x) clearTimeout(x);
  x = setTimeout(function() {
     // do the work here
     y++;
     console.log(y);
     // ----------------
  }, 1000);
});

So each time the button is clicked, it will only actually execute the code after a 1000 milliseconds, if the button is clicked in rapid succession, the timeout will just be cleared and start over again.

note that the above is untested

Personally I think the disabled solution is the best as it indicates to the user that he has clicked and something is happening, you can even show a loader next to the button as well.

Poplin answered 29/11, 2013 at 8:50 Comment(2)
I like this disabling button approach because It does not require me to know additional information. Any global variable or setinterval -> Makes code little bit complex.Kenley
This won't disable the button if it is being clicked programmatically.Shameful
I
5

As per edit

You should use .one()

or

You can unbind event on click function

var x = 1;
function myButtonClick() {

    $('#button').unbind('click');

    // Do something
    // Save some data on network
    x++;
    console.log(x);
    $('#button').bind('click', myButtonClick);
}

$('#button').bind('click', myButtonClick);
Incomprehensible answered 29/11, 2013 at 8:50 Comment(0)
T
1

You can make use of jQuery one() method as shown here

Thirion answered 29/11, 2013 at 8:49 Comment(0)
S
1

I'm using the simple solution below and its been working for me well :

var x = 1;
e.handled=false;
$('#button').click(function(e) {
  if(e.handled==false){
      e.handled=true;
      // Do something
      // Save some data on network
      x++;
      console.log(x);      
  }
});
Simitar answered 29/11, 2013 at 8:52 Comment(0)
H
1

You are looking for this

$( "button" ).one( "click", function(evt) {
  x++;
  console.log(x);
});

Or if u need a certain time interval to lapse between two effective clicks.

var last, diff;
$( "button" ).click(function( event ) {
  if (last){
    diff = event.timeStamp - last;
    if (diff >= SOMEVAL){
      x++;
      console.log(x);    
    }
  }
  last = event.timeStamp;          
});
Heliostat answered 29/11, 2013 at 8:53 Comment(0)
P
1

var btn = document.querySelector('#twofuns');
btn.addEventListener('click',method1);
btn.addEventListener('click',method2);
function method2(){
  console.log("Method 2");
}
setTimeout(function(){
  btn.removeEventListener('click',method1);
},5000);
function method1(){
  console.log("Method 1");
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Pramod Kharade-RemoveEventListener after Interval</title>
</head>
<body>
<button id="twofuns">Click Me!</button>
</body>
</html>

You can remove one listener among multiple in java-script like above.

Princedom answered 11/10, 2018 at 4:6 Comment(0)
T
1

Assuming you have attached an event listener to the button, one approach is to just remove the event listener from the button and attach it afterwards again if needed. To do so, add the following code in the event listener function itself:

function handleButtonClick(e) {
  e.target.removeEventListener("click", handleBackButton);
}

Depending on your setup this could work.

Th answered 26/1, 2019 at 21:9 Comment(0)
B
0

Try the following code

var x = 1;
var fewSeconds=10; /*10 seconds*/

$('#button').click(function() {
$('#button').attr("disabled", "disabled");
  x++;
  console.log(x);
    var btn = $(this);
    btn.prop('disabled', true);
    setTimeout(function(){
        btn.prop('disabled', false);
    }, fewSeconds*1000);

});
Bertelli answered 29/11, 2013 at 8:57 Comment(0)
E
-1

start the Busy Indicator on button click and stop it before return at the end

Function DoSomething(){
startBusyIndicator();
// perform Action //

stopBusyIndicator();
return;
}
Elaina answered 29/4, 2021 at 14:35 Comment(1)
No, such problems simply require debouncing or throttling the handler. The BusyIndicator you mentioned is a very complex solution and even impossible to make it stable for some protocols.Hybris

© 2022 - 2024 — McMap. All rights reserved.