[javascript] How to temporarily disable a click handler in jQuery?

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.

This question is related to javascript jquery

The answer is


Try utilizing .one()

_x000D_
_x000D_
var button = $("#button"),_x000D_
  result = $("#result"),_x000D_
  buttonHandler = function buttonHandler(e) {_x000D_
    result.html("processing...");_x000D_
    $(this).fadeOut(1000, function() {_x000D_
      // do stuff_x000D_
      setTimeout(function() {_x000D_
        // reset `click` event at `button`_x000D_
        button.fadeIn({_x000D_
          duration: 500,_x000D_
          start: function() {_x000D_
            result.html("done at " + $.now());_x000D_
          }_x000D_
        }).one("click", buttonHandler);_x000D_
_x000D_
      }, 5000)_x000D_
    })_x000D_
  };_x000D_
_x000D_
button.one("click", buttonHandler);
_x000D_
#button {_x000D_
  width: 50px;_x000D_
  height: 50px;_x000D_
  background: olive;_x000D_
}
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">_x000D_
</script>_x000D_
<div id="result"></div>_x000D_
<div id="button">click</div>
_x000D_
_x000D_
_x000D_


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


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

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

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

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

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


$("#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.


$("#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.


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.


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>

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