How to temporarily disable a click handler in jQuery?

JavascriptJquery

Javascript Problem Overview


Say I have something like the following to trap the click event of a button:

$("#button_id").click(function() {
  //disable click event
  //do something
  //re-enable click event
}

How do I temporarily disable the click event of the button until the end of the processing of the original click occurs? I basically have the div disappear after the button is clicked, but if the user clicks on the button fast several times, it processes all those clicks before the div gets a chance to disappear. I want to "debounce" the button so that only the first click gets registered before the div disappears.

Javascript Solutions


Solution 1 - Javascript

I noticed this post was old but it appears top on google and this kind of solution was never offered so I decided to post it anyway.

You can just disable cursor-events and enable them again later via css. It is supported on all major browsers and may prove useful in some situations.

$("#button_id").click(function() {

   $("#button_id").css("pointer-events", "none");
   //do something
   $("#button_id").css("pointer-events", "auto");
}

Solution 2 - Javascript

This is a more idiomatic alternative to the artificial state variable solutions:

$("#button_id").one('click', DoSomething);

function DoSomething() {
  // do something.

  $("#button_id").one('click', DoSomething);
}

One will only execute once (until attached again). More info here: http://docs.jquery.com/Events/one

Solution 3 - Javascript

$("#button_id").click(function() {
  if($(this).data('dont')==1) return;
  $(this).data('dont',1);
  //do something
  $(this).data('dont',0);
}

Remeber that $.data() would work only for items with ID.

Solution 4 - Javascript

You can unbind your handler with .off, but there's a caveat; if you're doing this just prevent the handler from being triggered again while it's already running, you need to defer rebinding the handler.

For example, consider this code, which uses a 5-second hot sleep to simulate something synchronous and computationally expensive being done from within the handler (like heavy DOM manipulation, say):

<button id="foo">Click Me!</div>
<script>
    function waitForFiveSeconds() {
        var startTime = new Date();
        while (new Date() - startTime < 5000) {}
    }
    $('#foo').click(function handler() {
        // BAD CODE, DON'T COPY AND PASTE ME!
        $('#foo').off('click');
        console.log('Hello, World!');
        waitForFiveSeconds();
        $('#foo').click(handler);
    });
</script>

This won't work. As you can see if you try it out in this JSFiddle, if you click the button while the handler is already executing, the handler will execute a second time once the first execution finishes. What's more, at least in Chrome and Firefox, this would be true even if you didn't use jQuery and used addEventListener and removeEventListener to add and remove the handler instead. The browser executes the handler after the first click, unbinding and rebinding the handler, and then handles the second click and checks whether there's a click handler to execute.

To get around this, you need to defer rebinding of the handler using setTimeout, so that clicks that happen while the first handler is executing will be processed before you reattach the handler.

<button id="foo">Click Me!</div>
<script>
    function waitForFiveSeconds() {
        var startTime = new Date();
        while (new Date() - startTime < 5000) {}
    }
    $('#foo').click(function handler() {
        $('#foo').off('click');
        console.log('Hello, World!');
        waitForFiveSeconds();

        // Defer rebinding the handler, so that any clicks that happened while
        // it was unbound get processed first.
        setTimeout(function () {
            $('#foo').click(handler);
        }, 0);
    });
</script>

You can see this in action at this modified JSFiddle.

Naturally, this is unnecessary if what you're doing in your handler is already asynchronous, since then you're already yielding control to the browser and letting it flush all the click events before you rebind your handler. For instance, code like this will work fine without a setTimeout call:

<button id="foo">Save Stuff</div>
<script>
    $('#foo').click(function handler() {
        $('#foo').off('click');
        $.post( "/some_api/save_stuff", function() {
            $('#foo').click(handler);
        });
    });
</script>

Solution 5 - Javascript

You can do it like the other people before me told you using a look:

A.) Use .data of the button element to share a look variable (or a just global variable)

if ($('#buttonId').data('locked') == 1)
    return
$('#buttonId').data('locked') = 1;
// Do your thing
$('#buttonId').data('locked') = 0;

B.) Disable mouse signals

$("#buttonId").css("pointer-events", "none");
// Do your thing
$("#buttonId").css("pointer-events", "auto");

C.) If it is a HTML button you can disable it (input [type=submit] or button)

$("#buttonId").attr("disabled", "true");
// Do your thing
$("#buttonId").attr("disabled", "false");

But watch out for other threads! I failed many times because my animation (fading in or out) took one second. E.g. fadeIn/fadeOut supports a callback function as second parameter. If there is no other way just do it using setTimeout(callback, delay).

Greets, Thomas

Solution 6 - Javascript

If #button_id implies a standard HTML button (like a submit button) you can use the 'disabled' attribute to make the button inactive to the browser.

$("#button_id").click(function() {
    $('#button_id').attr('disabled', 'true');

    //do something
    
     $('#button_id').removeAttr('disabled');
});   

What you may need to be careful with, however, is the order in which these things may happen. If you are using the jquery hide command, you may want to include the "$('#button_id').removeAttr('disabled');" as part of a call back, so that it does not happen until the hide is complete.

[edit] example of function using a callback:

$("#button_id").click(function() {
    $('#button_id').attr('disabled', 'true');
    $('#myDiv').hide(function() { $('#button_id').removeAttr('disabled'); });
});   

Solution 7 - Javascript

Try utilizing .one()

var button = $("#button"),
  result = $("#result"),
  buttonHandler = function buttonHandler(e) {
    result.html("processing...");
    $(this).fadeOut(1000, function() {
      // do stuff
      setTimeout(function() {
        // reset `click` event at `button`
        button.fadeIn({
          duration: 500,
          start: function() {
            result.html("done at " + $.now());
          }
        }).one("click", buttonHandler);

      }, 5000)
    })
  };

button.one("click", buttonHandler);

#button {
  width: 50px;
  height: 50px;
  background: olive;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div id="result"></div>
<div id="button">click</div>

Solution 8 - Javascript

it is better that use current event and dont save handler in global handler. i get current element event then unbind then bind again. for a handler.

var element =  $("#elemid")[0];
var tempHandler = jQuery._data(element)["events"]["click"][0].handler;
$("#elemid").unbind("click");

// do the job that click not suppose to listen;
$("#elemid").bind("click" , tempHandler );

for all handler

var element =  $("#elemid")[0];
var clickHandlerList = jQuery._data(element)["events"]["click"];
var handlerList = [];
for(var i  = 0 ; i <  clickHandlerList .length ; i++) {
    handlerList .push(clickHandlerList [i].handler);
}
$("#elemid").unbind("click");
// do the job that click not suppose to listen;
for(var i  = 0 ; i <  handlerList.length ; i++) {
    // return back all handler to element.
    $("#elemid").bind("click" , handlerList[i]);
}

Solution 9 - Javascript

I just barely ran into this problem when trying to display a loading spinner while I waited for a function to complete. Because I was appending the spinner into the HTML, the spinner would be duplicated each time the button was clicked, if you're not against defining a variable on the global scale, then this worked well for me.

    var hasCardButtonBeenClicked = '';
    $(".js-mela-card-button").on("click", function(){  
        if(!hasCardButtonBeenClicked){
            hasCardButtonBeenClicked = true;
            $(this).append('<i class="fa fa-circle-o-notch fa-spin" style="margin-left: 3px; font-size: 15px;" aria-hidden="true"></i>');
        }    
    });

Notice, all I'm doing is declaring a variable, and as long as its value is null, the actions following the click will occur and then subsequently set the variables value to "true" (it could be any value, as long as it's not empty), further disabling the button until the browser is refreshed or the variable is set to null.

Looking back it probably would have made more sense to just set the hasCardButtonBeenClicked variable to "false" to begin with, and then alternate between "true" and "false" as needed.

Solution 10 - Javascript

This example work.


HTML code:

  <div class="wrapper">
     <div class="mask">Something</div> 
  </div>

jQuery:

    var fade = function(){
		$(".mask").fadeToggle(500,function(){
			$(this).parent().on("click",function(){
				$(this).off("click");
				fade();
			});
		});
	};

	$(".wrapper").on("click",function(){
		$(this).off("click");
		fade();		
	});

Solution 11 - Javascript

$("#button_id").click(function() {
    $('#button_id').attr('disabled', 'true');
    $('#myDiv').hide(function() { $('#button_id').removeAttr('disabled'); });
}); 

Don't use .attr() to do the disabled, use .prop(), it's better.

Solution 12 - Javascript

This code will display loading on the button label, and set button to disable state, then after processing, re-enable and return back the original button text:

$(function () {

        $(".btn-Loading").each(function (idx, elm) {
            $(elm).click(function () {
                //do processing
                if ($(".input-validation-error").length > 0)
                    return;
                $(this).attr("label", $(this).text()).text("loading ....");
                $(this).delay(1000).animate({ disabled: true }, 1000, function () {
                    //original event call
                    $.when($(elm).delay(1000).one("click")).done(function () {
                        $(this).animate({ disabled: false }, 1000, function () {
                            $(this).text($(this).attr("label"));
                        })
                    });
                    //processing finalized
                });
            });
        });
        // and fire it after definition
    }
   );

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjackView Question on Stackoverflow
Solution 1 - JavascriptDefainView Answer on Stackoverflow
Solution 2 - JavascriptDave WardView Answer on Stackoverflow
Solution 3 - JavascriptThinkerView Answer on Stackoverflow
Solution 4 - JavascriptMark AmeryView Answer on Stackoverflow
Solution 5 - JavascriptChaosteView Answer on Stackoverflow
Solution 6 - JavascriptFerrousOxideView Answer on Stackoverflow
Solution 7 - Javascriptguest271314View Answer on Stackoverflow
Solution 8 - JavascriptmehdiView Answer on Stackoverflow
Solution 9 - JavascriptTyler EdwardsView Answer on Stackoverflow
Solution 10 - JavascriptAdnan MulalicView Answer on Stackoverflow
Solution 11 - Javascriptuser2836965View Answer on Stackoverflow
Solution 12 - JavascriptMohamed.AbdoView Answer on Stackoverflow