Javascript - JQuery on() clearTimeout scope issue

Go To StackoverFlow.com

1

I've searched and searched and I'm coming up empty on this one guys. Help! I'm watching an input field (#filter_query) for keyup events using JQuery on(). When the user punches in some text into this input field the on() triggers a setTimeout() which then in turns triggers a search function for the site. I'm trying to avoid the search from being fired if the user continues to type into the input field thus the clearTimeout(). For some reason the timeout ID isn't being preserved for clearTimeout and a new ID is being generated each time.

Here's my code:

$(document).on('keyup', '#filter_query', function (event) {
    var iTimeoutID,
        iTypingDelay = 800,
        sFilterVal = $.trim($('#filter_query').val()),
        sFilterCat = $('#filter_catagory').val(),
        sFilterCol = $('#filter_col').val();

    clearTimeout(iTimeoutID);

    if (sFilterVal !== "") {
        iTimeoutID = setTimeout(
                    function () {
                        searchFunction();
                    },
                    iTypingDelay
                    );
    }
});

With the above code my search function is being fired off several times regardless of the clearTimeout(). Thank you!

2012-04-03 21:08
by Nykad


0

Your iTimeoutID is a local variable to the .on() event handler function and thus it's destroyed each time that function completes and recreated the next time. Move it out of that scope to a higher level or to the global level so it can survive from one event to the next.

You can do it like this;

var iTimeoutID = null;
$(document).on('keyup', '#filter_query', function (event) {
    var iTypingDelay = 800,
        sFilterVal = $.trim($('#filter_query').val()),
        sFilterCat = $('#filter_catagory').val(),
        sFilterCol = $('#filter_col').val();

    if (iTimeoutID) {
        clearTimeout(iTimeoutID);
        iTimeoutID = null;
    }

    if (sFilterVal !== "") {
        iTimeoutID = setTimeout(function() {
            iTimeoutID = null;
            searchFunction();
        }, iTypingDelay);
    }
});

If you want to avoid global variables or you have more than one of these, you can use jQuery's .data() to store the timer id on the object like this:

$(document).on('keyup', '#filter_query', function (event) {
    var self = $(this);
    var iTimeoutID = self.data("timerID") || null;
    var iTypingDelay = 800,
        sFilterVal = $.trim($('#filter_query').val()),
        sFilterCat = $('#filter_catagory').val(),
        sFilterCol = $('#filter_col').val();

    if (iTimeoutID) {
        clearTimeout(iTimeoutID);
        iTimeoutID = null;
    }

    if (sFilterVal !== "") {
        iTimeoutID = setTimeout(function() {
            self.data("timerID", null);
            searchFunction();
        }, iTypingDelay);
    }
    self.data("timerID", iTimeoutID);
});

Here's one other version that uses a self-executing function to act as a shell for some variables that can last across the event handlers without being global:

(function() () {
    var iTimeoutID = null;
    $(document).on('keyup', '#filter_query', function (event) {
        var iTypingDelay = 800,
            sFilterVal = $.trim($('#filter_query').val()),
            sFilterCat = $('#filter_catagory').val(),
            sFilterCol = $('#filter_col').val();

        if (iTimeoutID) {
            clearTimeout(iTimeoutID);
            iTimeoutID = null;
        }

        if (sFilterVal !== "") {
            iTimeoutID = setTimeout(function() {
                iTimeoutID = null;
                searchFunction();
            }, iTypingDelay);
        }
    });
})();

If this was something quick and there was little chance of conflict with other code and there's only one object being served by the event handler, the first option is perfectly fine.

If the .on() event handler served multiple objects and each needed to keep track of it's own state, the 2nd option is perfect for that.

If you don't have multiple objects on the same event handler, then the third option is the simplest way to keep from adding any new global variables.

2012-04-03 21:12
by jfriend00
Hey thanks a bunch! This will work out just fine - Nykad 2012-04-03 21:25
Added one more option - a self executing anonymous function that acts as a shell for non-global variables that will last across the event handler - jfriend00 2012-04-03 23:32


0

Because you are defining the variable var iTimeoutID, that means that it's not a global variable and only accessible inside that function. So when the function gets called again, it creates a new variable.

I believe you should be able to fix it by not declaring the variable var iTimeoutID.

I might be wrong so if so, someone please correct me.

2012-04-03 21:12
by LeeR
Thanks but I've always thought using global variables like that is discouraged. Am I incorrect? If so what is best practice for declaring global variables - Nykad 2012-04-03 21:16
jfriend00 answer is good. Have a read of that one - LeeR 2012-04-03 21:19