Instead of including a debounce plugin library for a very small form section of my site, I've implemented some of David Walsh's JavaScript Debounce Function in my code to allow a small wait for slower user input, however I am binding it to the input field using jQuery instead of regular DOM event listener. The code works fine but my main question here is some peer input on if this is ok to do (will it cause unforeseen issues? is there a better method?) I couldn't find another example on this scenario on stack overflow but if it has been already asked, please do point me in the right direction.
$(function() {
// debounce function by David Walsh
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
// allow .5s for slower user input before processing
var debounceCustomInput = debounce(function() {
// if both inputs valid
if ($("#left").val().length > 0 && $("#right").val().length > 0) {
// process form
myAjaxFunction();
}
}, 500);
// event binding
$(document).on("input", "#left,#right", function() {
// debounce input
debounceCustomInput();
});
});
Related
I have a computationally heavy section of code which performs filtering of a data-set by DOM-manipulations. I made it async in order to ensure acceptable performace for large number of fiter items:
.on('input', function {
setTimeout(function(){
// heavy computation
}, 0)
})
The problem is that this section probably run on every user input change but only the result of the last one is of interest. So, my question is if there is a way to cancel the previous "threads" executing the heavy computation section and only execute the latest?
The best idea I have thus far is to use some sort of semaphore (possibly $.Deferred) and try to reduce the critical section.
If you only need the final result, use debounced event handler. This is a sample debounce implementation, but there are other on the web.
Wrapping your event handler with debounce will prevent the event handler from firing, as long as it's called repeatedly. The handler will fire, only if the handler is called, and then it's idle for a set amount of time.
.on('input', debounce(function {
// heavy computation
}, 500)) // wait 500ms before firing the handler
In the example type continuously and stop, you'll see that the console will log the word called only once:
var debounce = function(func, wait, immediate) {
var timeout;
return function() {
var context = this,
args = arguments;
var later = function() {
timeout = null;
if ( !immediate ) {
func.apply(context, args);
}
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait || 200);
if ( callNow ) {
func.apply(context, args);
}
};
};
$('#input').on('input', debounce(function() {
console.log('called');
}));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="input">
I am working on building a search feature into my site. To do this I have a search bar which sends an AJAX request to my server each time a user types something. The server will in turn send back the items which match the search.
The only problem with this currently is that if a user types "a" and then "b" what it will send is:
a
ab
To counter this I have found setTimeout which delays when the user enters a search; however, currently it is only delaying when the strings fire (i.e. waits 0.75 seconds before sending a and then waits 0.75 seconds before sending ab).
Here's the JS:
$('#searchBox').keyup(function(e) {
var timeoutID = setTimeout(searchRequest, 750, $(e.currentTarget).val());
});
function searchRequest(str) {
if (str.length > 0) {
console.log('search request initalized | sending: ', str);
var xhttp = new XMLHttpRequest();
xhttp.open('POST', 'code to send here', true);
xhttp.send(str);
}
}
I think what you need is a debounce function.
Here's the basic JavaScript debounce function (as taken from Underscore.js):
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
You can use it to debounce click:
$('#searchBox').keyup(debounce(function(e) {
searchRequest$(e.currentTarget).val());
}, 750));
You have to clear thew timeout to make it work. Have a look on this link ;) Resetting a setTimeout
Basically, when the user added a letter, you check if you already defined a timeout. If you defined a timeout you clear it. Then you reset the timeout. Something like that
var yourTimeout;
function sendInfo(info){
if (yourTimeout != undefined)
clearTimeout(yourTimeout);
yourTimeout = setTimeout(function(){
//do something
}, 500);
}
In your case, the sendInfo function is the keyup handler, and you call searchRequest in the timeout like you already did ;)
Hope it helps
I have a wheel event setup but if you are using OS X, the event continues to fire because of the native elastic effect.
How do I prevent this elastic effect?
Here is some code...
window.addEventListener('wheel',function(){
pxCount++;
var starContainer =
document.getElementById('starContainer').style.left = '-'+50*pxCount+'px';
});
Here is the entire project http://codepen.io/www139/pen/wKbOJz
You could wrap your listener in a debounce function, the purpose of which being that a certain action is only performed once in a given time limit.
I'm a fan of this one: https://davidwalsh.name/javascript-debounce-function
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
You'd probably use it like this:
var wheelAction = debounce(function() {
pxCount++;
var starContainer =
document.getElementById('starContainer').style.left = '-'+50*pxCount+'px';
}, 250);
window.addEventListener('wheel', wheelAction);
I'm trying to debounce keystrokes in a form input on IE Mobile 6 (from what I gather, about on par with IE 3-4 in terms of support).
Due to the lack of support, I can't add event listeners after declaration (i.e., document.getElementById('elementId').addEventListener(...) doesn't work), I can only do them inline, like onkeydown="doSomething()".
Here is a jsBin.
So, with this debounce function (taken from David Walsh):
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this, args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
The recommended way to set up your event function would be like:
var doSomething = debounce(function() { ... }, 250);
However, I can't use this doSomething style function in the inline event listeners on IE Mobile 6.
So, in the markup, I've tried:
<input type="text" onkeydown="doSomething()" />
And
<input type="text" onkeydown="doSomething()()" />
And in javascript:
// return the result of debounce()
function doSomething() {
return debounce(function() { ... }, 250);
}
// just debounce()
function doSomething() {
debounce(function() { ... }, 250);
}
// return the result of the returned function of debounce, aka debounce()()
function doSomething() {
return debounce(function() { ... }, 250)();
}
I've also just tried putting the whole contents of the debounce function inside of this function, like:
function doSomething() {
var timeout, func, wait, immediate;
func = function() {
console.log('test');
};
wait = 250;
return function () {
var context = this, args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
So, long question short:
How can I write this exact statement:
var doSomething = debounce(...);
Like this:
function doSomething() {
}
You seem to have a pretty unique situation. Given that your target browser (IE Mobile 6) implements pretty old technologies (ECMAScript 3 and elements from IE 8), you won't be able to use the standard addEventListener method. Part of your problem is likely due to a mixture of the context limitations of using inline javascript along with the fact that you're programming against an old, old (1999) version of JS. Let's see how this works.
EDIT
Also make sure you're wrapping your code in some kind of document ready. I'll use jQuery to do that. That could be why your JS isn't executing.
HTML
<input type='text' id='theInput' />
<script src='yourDebounceScript.js'></script>
JS
// by the time this is executed,
// DOM elements above this script will be ready
(function() {
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this, args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
var input = document.getElementById('theInput');
input.attachEvent('keydown' function() {
// wrap your debounce function in an anonymous function
// so that you can pass arguments to the debounce as needed
debounce(function() { /* ... */ }, 250, true);
});
})();
If you can use jQuery:
// make sure the document is ready before trying to access the DOM
$(function() {
// your debounce function here...
// jQuery will handle the version compatibility for you
$('#theInput').keydown(debounce);
});
I want to write a scroll callback function that gets called after every one second when user scrolls. I tried
window.on('scroll', function(e) {
//scroll function body
});
But the problem is it gets called everytime user scrolls even one pixel. Can someone please suggest a solution.
You can write a function like this
scrollCb = function(func, later) {
var timeout;
return function() {
var context = this, args = arguments;
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
and call it like
scrollCb(function() {
//Your scroll callback handler
}, 1000);