I have a #search
element, which when the keyup
event occurs should fire a function. This function should only fire if keyup
hasn't occurred in a set amount of time (say 500 milliseconds for example). This will prevent search results from updating every letter that is pressed. The problem is that with backbone.js, I have my events in a hash and the one that is applicable looks like:
'keyup #search' : 'setSearch'
which calls the setSearch()
function when the keyup
event occurs. I'm not really clear on how to handle it at this point. I've tried a variety of things, but nothing can maintain the timer past the function ending.
I have something like so:
setSearch: function(event) {
var timer = window.setTimeout( function() {
// run function here
alert('fired');
}, 500);
},
rather than the alert('fired')
, I'll have my own function run. I can see why this code doesn't work (a timer is set for every keyup
event that occurs. But I still don't have a clear idea on what else I could try.
I have a #search
element, which when the keyup
event occurs should fire a function. This function should only fire if keyup
hasn't occurred in a set amount of time (say 500 milliseconds for example). This will prevent search results from updating every letter that is pressed. The problem is that with backbone.js, I have my events in a hash and the one that is applicable looks like:
'keyup #search' : 'setSearch'
which calls the setSearch()
function when the keyup
event occurs. I'm not really clear on how to handle it at this point. I've tried a variety of things, but nothing can maintain the timer past the function ending.
I have something like so:
setSearch: function(event) {
var timer = window.setTimeout( function() {
// run function here
alert('fired');
}, 500);
},
rather than the alert('fired')
, I'll have my own function run. I can see why this code doesn't work (a timer is set for every keyup
event that occurs. But I still don't have a clear idea on what else I could try.
4 Answers
Reset to default 18What you are looking for is actually a function provided to you from underscore.js (a requirement of Backbone)
setSearch: _.throttle(function() {
//Do Stuff
}, 500),
In a nutshell, this returns a new form of the anonymous function that can only be called once every 500ms. You will likely have to tweak the timing to your needs.
More Info: http://documentcloud.github.com/underscore/#throttle
You need an instance variable in your view that stores the timer ID, then you can stop it and restart it as needed:
setSearch: function(event) {
var self = this;
if(self.timer)
clearTimeout(self.timer);
self.timer = setTimeout(function() {
alert('fired');
self.timer = null;
}, 500);
}
So, if the timer is already running, you call clearTimeout
to stop it, start a new timer, and store the timer ID in self.timer
(AKA this.timer
). You'll also want to reset the stored timer ID in the timer's callback function or your setSearch
won't do anything after its timer has fired once. And all the self
business is just to capture this
for use in the timer's callback function.
Preventing the updating of search results on every keyup
is exactly the kind of situation that Underscore's _.debounce(function, wait)
function is meant to deal with. The underscore documentation for _.debounce() states:
Creates and returns a new debounced version of the passed
function
which will postpone its execution until afterwait
milliseconds have elapsed since the last time it was invoked. Useful for implementing behavior that should only happen after the input has stopped arriving.
Your refactored code would look as simple as:
setSearch: function(event) {
_.debounce(doSomething, 300);
},
Since you want your event handler events to be able to maintain whether or not an event has recentlyFired
, you probably want to wrap your handler into a closure and maintain that status. The status should be changed to true when an event has fired, and reset to false after a delay of 500ms.
setSearch: function( ) {
var firedRecently = false;
return function(event) {
if (firedRecently) {
// it has fired recently. Do you want to do something here?
} else {
// not fired recently
firedRecently = true;
// run your function here
alert('fired');
var resetStatus = window.setTimeout( function () {
firedRecently = false;
}, 500);
}
}
}( );