The why
a. Real life problem: user goes in and starts typing in a simple input form. We want to show him accurate results ( more often than not, requesting new data from a server ).
b. Real life example: We want to search in a list of states and show only the ones that match ( partially ) the user's input.
The problem
Take 1.b example and do that on "keypress", "change", [whatever_event_you_want_here]. Now you have a request on each keystroke. So on every key pressed by a user that means a new request on the server and/or some data processing using our mighty and all powerful JS.
This is in most cases pointless, the most relevant example being pressing space and then backspace as the user instantly changes his mind.
Why should I care, you might ask?
Also,
- less js processing means less power consumption ( a lot of traffic is from mobile, don't ignore it anymore )
- most of the pages are already js heavy, always adding extra dom changes and js processing without remorse will kill the page eventually
Another good example is when the person searching knows exactly what he or she is looking for : "Alabama". Most people write fast enough these days ( some random statistics found on the internet about this average about 150ms between 2 consecutive keystrokes, when excluding punctuation - which is the case for us ! ) . So consider this : a request is made for each of the following : "a", "al", "ala", "alab", "alaba" "alabam", "alabama". Taking a look at XHR requests, your network tab would look like this :
And that is for a single search from a single user.
The concept
Ignoring the same repetitive event on the same input element if it gets triggered multiple times in a given amount of time is known in the javascript ecosystem as "debouncing". A clasic debounce function takes 2 arguments: the function to be executed, and the delay.
It is implemented in jQuery. It exists as a support function in both lodash and underscore. If you already have one of this libraries included in your project, you are good to go.
But what happens if you don't? Should you add a new dependency to the project just because you did not know how easy it is to write it yourself ?
Bare with me and I will give you both a working example and an eli5 of how things work.
The how
Let's say we have a simple input that must do something at "keyup":
<input type="text" @keyup="setFilteredStates()">
Now let's wrap the setFilteredStates inside our debounce function. It will look something like this:
<input type="text" @keyup="debounce($event.target.value, 250)">
And the debounce function :
debounce(search, debounceDuration = 300){
if(this.timeoutId !== null)
{
clearTimeout(this.timeoutId);
}
this.timeoutId = setTimeout( _ => {
this.setFilteredStates(search);
}, debounceDuration);
}
What happens is quite simple, actually. We let the javascript know that we want the function to get called after an arbitrary number of milliseconds, in our case 250. We can do that using setTimeout. All nice and easy so far, yes?
Next thing we want to do is to cancel the execution of the previous function, if it was not yet called. We can do that quite easily, as setTimeout returns an ID. So we call clearTimeout(ID) and voila, only the last key event will actually trigger the setFilteredStates, and only after 300ms have passed after last user's input.
Outro
The example is written using VueJs, but the same concept would work in vanilla or combined with any other library.
The complete snippet can be found here :
https://codepen.io/costicaaa/pen/xMorgO
For simplicity, the setFilteredStates does not actually make network calls, only filters a hard-coded array of states in JS.
I did not get into details about the binding of "this" inside or the arrow functions as this would get too long and there are already a lot of other great tutorials out there.
Open to suggestions / feedback . Also, if I got anything wrong, please let me know .
Cheers :)
Top comments (2)
Debouncing comes from analogue electronics. A switched input 'rings' just after turning on or off due to the various resistances, capacitances and inductances in the circuitry being used.
Usually fixed by applying a low pass filter to the circuitry where the input is used.
This appears to be a similar solution.
Something new everyday, thanks for the analogy :)