Search code examples
javascriptfunctionterminatedebouncing

How to kill a Javascript function if it is called again?


I have a search box on my web page that has check boxes in order for the user to filter their results. Only one check box can be checked at once.

When a check box is clicked my code runs off and applies the filter to the list and returns the correct results.

The problem I have is that when a check box is clicked multiple times in quick succession, it queues the requests and pulls them back one by one. This can take a while if a check box is checked and then un-checked multiple times.

Is there any way in Javascript to inform the function that it has been called again and it should stop everything other than this last request?


Solution

  • You want to wrap your onclick callback in a debouncing function like

    http://underscorejs.org/#debounce

    Say you have this

    function search() {
        // ...
    }
    
    $jquery(".myFilterCheckboxes").click(search);
    

    You should be able to just change the above to:

    // Only allow one click event / search every 500ms:
    $jquery(".myFilterCheckboxes").click(_.debounce(search, 500));
    

    There are tons of debouncing functions out there, and writing your own isn't a big deal really if you can't or don't want to include underscore.js.

    My first thought was towards debouncing because you mentioned multiple clicks creating multiple events in a short period. Debouncing is used really often for things like type-ahead search or autocomplete to provide a little space between key presses for thinking time.

    As others have mentioned it may make more sense to simply disable the checkboxes / click event while your search is running. In that case, try something like this:

    function disableClick(elem) {
      elem.unbind("click");
      elem.attr("disabled", true);
    }
    
    function enableClick(elem, onclick) {
      // Enable click events again
      elem.live("click", search);
      // Enable the checkboxes
      elem.removeAttr("disabled");
    }
    
    function search() {
      var boxes = $jquery(".myFilterCheckboxes");
      disableClick(boxes);
      $.get(...).always(function() {
        enableClick(boxes, search);
      });
    }
    
    $jquery(".myFilterCheckboxes").live("click", search);
    

    Why disable the click event, add the disabled attribute to the checkboxes instead of just a global lock variable? Well, global locks can be somewhat error prone, but more than that, we already have a global object that matters in the DOM. If we just modify the DOM state we get the right behavior and signal to our users that they should chill out on the checkboxes until the search completes.

    That said, it probably makes sense with any kind of locking / unbinding scenario to indicate to the user with a loading spinner or something that you're doing work.