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.


Solution 1:

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:

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:

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

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>