40

How can I temporarily disable the onclick event listener, (jQuery preferred), after the event has been fired?

Example:

After the user clicks on the button and fires this function below, I want to disabled the onclick listener, therefore not firing the same command to my django view.

$(".btnRemove").click(function(){
   $(this).attr("src", "/url/to/ajax-loader.gif");
   $.ajax({
        type: "GET",
        url: "/url/to/django/view/to/remove/item/" + this.id,
        dataType: "json",
        success: function(returned_data){
            $.each(returned_data, function(i, item){
              // do stuff                       
     });
   }
});

Thanks a lot,

Aldo

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
aldux
  • 2,774
  • 2
  • 25
  • 36
  • how temporarily? what has to happen before you want to accept click events again? – Ty W Dec 17 '09 at 13:42
  • 9
    (PS. don't allow GET requests to perform state changes.) – bobince Dec 17 '09 at 13:47
  • 2
    @bobince: Oh, wouldn't it be great if we all obeyed that simple rule... @OP: More on what bob's talking about (in frighteningly turgid prose) here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2 – T.J. Crowder Dec 17 '09 at 13:51
  • Maybe I haven't explained it all: I have a list of items in a shopping cart. Every item has an image witch it's id is the item id and has the class="btnRemove" (I know its not a button). When the user clicks the button to remove it, I change the button to a preloader image. What I want is: when the image turns to loading, it does not accept click anymore, therefore no firing the same command. @bobince: You are right, but I think this is a cool way to remove the itens without reloading the page. – aldux Dec 17 '09 at 14:12

10 Answers10

66

There are a lot of ways to do it. For example:

$(".btnRemove").click(function() {
    var $this = $(this);
    if ($this.data("executing")) return;
    $this
        .data("executing", true)
        .attr("src", "/url/to/ajax-loader.gif");
    $.get("/url/to/django/view/to/remove/item/" + this.id, function(returnedData) {
        // ... do your stuff ...
        $this.removeData("executing");
    });
});

or

$(".btnRemove").click(handler);

function handler() {
    var $this = $(this)
        .off("click", handler)
        .attr("src", "/url/to/ajax-loader.gif");
    $.get("/url/to/django/view/to/remove/item/" + this.id, function(returnedData) {
        // ... do your stuff ...
        $this.click(handler);
    });
}

We can also use event delegation for clearer code and better performance:

$(document).on("click", ".btnRemove:not(.unclickable)", function() {
    var $this = $(this)
        .addClass("unclickable")
        .attr("src", "/url/to/ajax-loader.gif");
    $.get("/url/to/django/view/to/remove/item/" + this.id, function(returnedData) {
        // ... do your stuff ...
        $this.removeClass("unclickable");
    });
});

If we don't need to re-enable the handler after it has been executed, then we can use the .one() method. It binds handlers that are to be executed only once. See jQuery docs: http://api.jquery.com/one

thorn0
  • 9,362
  • 3
  • 68
  • 96
19

For how long do you want to disable the click event listener? One way is to unbind the event listener using jQuery's unbind http://docs.jquery.com/Events/unbind.

But it's best-practice not to unbind an event only to rebind it later. Use a boolean instead.

var active = true;
$(".btnRemove").click(function() {
    if (!active) {
        return;
    }
    active = false;
    $(this).attr("src", "/url/to/ajax-loader.gif");
    $.ajax({
        type: "GET",
        url: "/url/to/django/view/to/remove/item/" + this.id,
        dataType: "json",
        success: function(returned_data) {
            active = true; // activate it again !
            $.each(returned_data, function(i, item) {
                // do stuff                       
            });
        }
    });
});

edit: to be safe you should also care about the other ajax completion routines (there are only three: success, error, complete see docs) or else active might stay false.

RamboNo5
  • 2,050
  • 2
  • 17
  • 26
  • how does this do the job, active is nothing but a boolean being read at the beginning – TStamper Dec 17 '09 at 13:56
  • 2
    `active` stays false up until the ajax request finishes with a success. during that time the click-handler immediately returns at the beginning, so it is basically not excecuting. – RamboNo5 Dec 17 '09 at 14:03
  • I loved the simplicity of returning false...I don't know if it's better practice than somehow stop propagation, but it was easier and worked in my case =) – Edward May 24 '13 at 16:40
  • Won't this just always return, no matter what...? – Taylor A. Leach Jun 07 '18 at 16:43
  • @TaylorA.Leach Now that you mention it, I think the first check in the if statement should be `if (!active)`. I'll update the answer. – RamboNo5 Jul 01 '18 at 08:23
5

why not disable the button ?Any specific reason that you want to disable this listner alone ? BTB, from your code, I see that you are making an ajax call. SO you specifically want to block user until the call comes back ? If yes, you can try blockUI, a jQuery plugin

ram
  • 11,468
  • 16
  • 63
  • 89
3

I would setup a global variable to keep track of AJAX requests...

var myApp = {
  ajax: null
}

And then have this little bit of magic to stop simultaneous requests...

// Fired when an AJAX request begins
$.ajaxStart(function() { myApp.ajax = 1 });

// Fired when an AJAX request completes
$.ajaxComplete(function() { myApp.ajax = null });

// Fired before an AJAX request begins
$.ajaxSend(function(e, xhr, opt) {
  if(myApp.ajax != null) {
    alert("A request is currently processing. Please wait.");
    xhr.abort();
  }
});

With this approach, you should not have to go back through your code and modify every single one of your AJAX calls. (something I call an "append" solution)

Josh Stodola
  • 81,538
  • 47
  • 180
  • 227
  • As of jQuery 1.9, all the handlers for the [jQuery global Ajax events](http://api.jquery.com/category/ajax/global-ajax-event-handlers/), including those added with the .ajaxStart() method, must be attached to document. – ooXei1sh Dec 19 '15 at 15:04
2

You could make the action within the click based upon a boolean value. When it's clicked, change the boolean value and uset setTimeout() to change it back after a few seconds. That would effectively limit the user to clicking the button only once every few seconds.

var isEnabled = true;

$("a.clickMe").click(function(e){
  e.preventDefault();
  if (isEnabled == true) {
    isEnabled = false; // disable future clicks for now
    do_Something();
    setTimeout(function(){
      isEnabled = true;
    }, 3000); // restore functionality after 3 seconds
  }
});
Sampson
  • 265,109
  • 74
  • 539
  • 565
2

I would use a class eg 'ajax-running'. The click event would only be executed if the clicked element does not have the 'ajax-running' class. As soon you ajax call finishes you can remove the 'ajax-running' class so it can be clicked again.

$(".btnRemove").click(function(){
    var $button         = $(this);
    var is_ajaxRunning  = $button.hasClass('ajax-running');
    if( !is_ajaxRunning ){
        $.ajax({
            ...
            success: function(returned_data) {
                ...
                $button.removeClass('ajax-running');
            });
        };
    }   
});
Mariano Cavallo
  • 950
  • 11
  • 16
1

I'd suggest disabling the button, then re-enabling it in your Ajax completion routines (success or failure, remember). If you're worried about the browser not respecting your disabling the button, you can back that with your own flag on the button (e.g., set an attribute called data-disabled, using the data- prefix as good practice and to be HTML5 compatible). But barring actually running into a problems with browsers not disabling the button, I'd probably consider that good enough.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1
var ajaxAction = function() {
    var ele = $(this);
    ele.unbind("click", ajaxAction);
    ele.attr("src", "/url/to/ajax-loader.gif");
    $.ajax({
        type: "GET",
        url: "/url/to/django/view/to/remove/item/" + this.id,
        dataType: "json",
        success: function(returned_data) {
            $.each(returned_data, function(i, item) {
            });
        },
        complete: function() {
            ele.bind("click", ajaxAction);
        }
    });
}
$(".btnRemove").click(ajaxAction);
jitter
  • 53,475
  • 11
  • 111
  • 124
  • I was actually going to add exactly this. Much cleaner to name it and unbind/rebind internally than all the other hoops being jumped through in these examples. – Erik Reppen Jul 17 '12 at 16:14
1

If you are posting via ajax, you can disable the button on click and enable it after the process completes. But if you are posting back, you cannot just disable the button. Disabling the button causes the server side click event not to fire. So just hide the button on click and show user friendly message. The post on how to disable asp.net button on postback helps.

Sangam Uprety
  • 1,482
  • 2
  • 20
  • 38
0

you could also just hide the button (or the containing div)

$(".btnRemove").click(function() {
   $(this).hide();
   // your code here...
})

and you can just call .show() if you need to display it again.

Also depending on your use case you should consider using a loading overlay instead