Debounce function in Jquery? - javascript

Been looking for a debounce function or way to debounce in Jquery. The build up of animations can get super annoying.
Heres the code:
function fade() {
$('.media').hide();
$('.media').fadeIn(2000);
}
var debounce = false;
function colorChange() {
if (debounce) return;
debounce = true;
$('.centered').mouseenter(function() {
$('.centered').fadeTo('fast', .25);
});
$('.centered').mouseleave(function() {
$('.centered').fadeTo('fast', 1);
});
}
function colorChange2() {
$('.centered2').mouseenter(function() {
$('.centered2').fadeTo('fast', .25);
});
$('.centered2').mouseleave(function() {
$('.centered2').fadeTo('fast', 1);
});
}
function colorChange3() {
$('.centered3').mouseenter(function() {
$('.centered3').fadeTo('fast', .25);
});
$('.centered3').mouseleave(function() {
$('.centered3').fadeTo('fast', 1);
});
}
function closerFade() {
$('.closer').hide();
$('.closer').fadeIn(2000);
}
I wrapped those all in $(document).ready(function() {
Is there way to debounce??

I donĀ“t like the idea to include a library just for a debounce function. You can just do:
var debounce = null;
$('#input').on('keyup', function(e){
clearTimeout(debounce );
debounce = setTimeout(function(){
$.ajax({url: 'someurl.jsp', data: {query: q}, type: 'GET'})
}, 100);
});

I would just include underscore.js in my project and use the debounce function that it contains. It works great. I've used it in multiple projects.
http://underscorejs.org/#debounce
debounce_.debounce(function, wait, [immediate]) Creates and returns a
new debounced version of the passed function which will postpone its
execution until after wait milliseconds have elapsed since the last
time it was invoked. Useful for implementing behavior that should only
happen after the input has stopped arriving. For example: rendering a
preview of a Markdown comment, recalculating a layout after the window
has stopped being resized, and so on.
At the end of the wait interval, the function will be called with the
arguments that were passed most recently to the debounced function.
Pass true for the immediate argument to cause debounce to trigger the
function on the leading instead of the trailing edge of the wait
interval. Useful in circumstances like preventing accidental
double-clicks on a "submit" button from firing a second time.
var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);

Related

How to overwrite setTimeout before it reach the time set?

So I'm doing an autocomplete search using jquery. I have to set a delay before executing the ajax function because I don't want to hammer my server with calls every time I type on a textbox. Here is my code:
function searchVendor() {
setTimeout(searchVendor2, 5000);
}
function searchVendor2() {
var search = $('#inputVendor').val();
$.ajax({
type: 'POST',
url: '/getVendors',
data: {search: search},
dataType: 'json',
success: function(s) {
$('#inputVendor').autocomplete({source: s});
}
});
}
so the function searchVendor is executed onkeyup
<input type="text" class="form-control input-sm" id="inputVendor" onkeyup="searchVendor()">
If I type 3 characters (ex. sas) then the function searchVendor2 is executed 3 times. The 5 seconds delay works but it didn't stop and overwrite the previous setTimeout.
What I want to happen is, if I type a character on the textbox it will be executed after 5 seconds, BUT! if a new character is typed before the 5 seconds, setTimeout is reset again to 5 seconds. As long as the user is typing on the textbox the setTimeout is reset to 5 seconds and it will ONLY be executed if the 5 seconds elapsed without the user typing again.
Thanks to those who can help!
First, you need to save your timeout id in a global variable, or in a variable that can be accessed later when the function is called again.
Now, whenever your function is called, first you clear that timeout if it exists. Thus you clear any pre-existing timeouts and set a new one every time the function is called.
var myTimeout;
function searchVendor() {
clearTimeout(myTimeout);
myTimeout = setTimeout(searchVendor2, 5000);
}
function searchVendor2() {
var search = $('#inputVendor').val();
$.ajax({
type: 'POST',
url: '/getVendors',
data: {search: search},
dataType: 'json',
success: function(s) {
$('#inputVendor').autocomplete({source: s});
}
});
}
The other answers involving setTimeout() are simple and will work, but this is one of the few times when I would recommend using a utility library as they can go a few steps further in a way that is noticeable to the user.
It is also important that we avoid re-inventing the wheel. And in this case, what you want is a debounce or throttle function to limit the number of times your handler gets executed within a given time span. The good libraries also accept options to tweak when exactly your handler gets run, which can affect the responsiveness of your app.
Read more about debouncing and throttling.
For your use case, I would recommend Lodash's _.throttle() with both leading and trailing options set to true. This will ensure that long entries of text will still get some intermediate results, while also getting results as fast as possible (not having to wait for a timer the first time around) and still guaranteeing that the final keystroke will trigger a new result, which not all debounce settings would do.
const handler = (evt) => {
console.log('I will talk to the server.');
};
const throttled = _.throttle(handler, 500, {
leading : true,
trailing : true
});
Then register the throttled function as the event listener.
<input type="text" class="form-control input-sm" id="inputVendor" onkeyup="throttled()">
You must clear the timeout when you want to stop it. Instead of just doing this:
var timeoutId;
function searchVendor() {
timeoutId = setTimeout(searchVendor2, 5000);
}
you should add clearTimeout(timeoutId);, like this:
var timeoutId;
function searchVendor() {
clearTimeout(timeoutId);
timeoutId = setTimeout(searchVendor2, 5000);
}
You can use minLength of autocomplete so that the API is not called as soon as the user starts typing.
Here is the reference from autocomplete
minLength: The minimum number of characters a user must type before a search is performed
$( "#inputVendor" ).autocomplete({
minLength: 3
});
If you want on every keypress, as other answers suggested, you can use clearTimeout to remove the old timeout.
var timeout;
function searchVendor() {
clearTimeout(timeout);
timeout = setTimeout(searchVendor2, 5000);
}
The setTimeout and setInterval functions can be stopped using clearTimeout or clearInterval functions, passing the unique identifier generated by the first ones. For example, here you have a small code that helps you to understand this:
var delay;
document.addEventListener("mousemove", function () {
callSetTimeout();
});
function callSetTimeout () {
if (!isNaN(delay)) { clearTimeout(delay); }
delay = setTimeout(function () {
console.log("do something");
}, 5000);
}
I change the timeout but I check with interval
var goal={
cb: cb
, timeout: (+new Date())+60000 //one minute from now
, setInterval(function(){
if((+new Date())>goal.timeout){cb();}
})
};
Now I want to increase, decrease or reset the timeout?
goal.timeout-=1000;
goal.timeout+=1000;
goal.timeout=60000;

How to make jQuery wait after event

I have some text boxes that uses AJAX and will process what the user types as they're typing. The problem is that the process that happens after they type is a pretty loaded one. Is there anyway that I can make the event wait 500 msec or so before it will process the event again? So let's say I type a, and then 200ms after I type bsdke. The event will only process a coming in. Let's say I then type asdf 600ms after I typed a. The event will then trigger and process absdkeasdf because it has been 500ms after the last process.
Or even, is there a way I can make the event not become triggered until the previous event that was triggered has finished what it was doing?
Is it possible? Surely sticking a timeout function at the end isn't going to work, right?
$('#selection1-3-4-a, #selection1-3-4-b, #selection1-3-4-c, #selection1-3-4-d, #selection1-3-4-e, #selection1-3-4-f').on('input', function (e) {
var url = "/ex.php";
$.ajax ({
//......
});
It can get hard to tune the event so the users gets a nice experience and you don't flood the server with requests when using autocomplete functions.
I would do it this way:
The event will wait X miliseconds to run itself. If the event is fired again, and the X miliseconds haven't passed, you reset the delay to 0.
Let's be cool and make a generic function to do this (couldn't test it, it might be broken):
function delayedEvent(eventHandler, delay) {
var lastTimeout = null;
return function () {
var that = this,
args= Array.prototype.slice.call(arguments).sort();
if (lastTimeout !== null)
window.clearTimeout(lastTimeout);
lastTimeout = window.setTimeout(function () {
eventHandler.apply(that, args);
}, delay);
};
}
$('... selector ...').on('event', delayedEvent(function () { ... your code ... }, 500));
Use setTimeout:
var timer;
$('#selection1-3-4-a, #selection1-3-4-b, #selection1-3-4-c, #selection1-3-4-d, #selection1-3-4-e, #selection1-3-4-f').on('input', function (e) {
var url = "/ex.php";
clearTimeout(timer);
timer = setTimeout(function() {
$.ajax ({
//......
}, 500);
});
I would recommend using a lock.
// global lock
var locked = false;
$('#selection1-3-4-a, #selection1-3-4-b, #selection1-3-4-c, #selection1-3-4-d, #selection1-3-4-e, #selection1-3-4-f').change(function (e)
{
if(!locked)
{
locked = true;
$.ajax(......) // ajax here
setTimeout(function(){locked = false;}, 500);
}
});

Run JavaScript immediately but wait x seconds before it can be run again?

Im listening for an event and I need to run a function (in this example console log for demoing my code) when it happens.
This is working however the event happens multiple times in quick succession and I only want the function to run once. How can I run the function straight away but then wait a second before its able to be triggered again?
$(document).on('someEvent', function(event, data) {
if (var === 'something') {
console.log('Run');
}
});
Update: To be clear, I need to wait for the event 'someEvent' to occur before my console function runs.
Some like that?
var is_blocked = false;
var block = function( time_to_wait ) {
is_blocked = true;
setTimeout( function() {
is_blocked = false;
}, time_to_wait );
};
$(document).on('someEvent', function(event, data) {
if ( is_blocked === false ) {
block( 1000 );
console.log('Run');
}
});
If you don't mind using an external library, use lodash's debounce method. Note that the sample in the docs is pretty similar to the case you described. The options (leading/trailing) can be used to customize the behavior.
There's a tiny library called underscore.js that has a ton of useful functions. Among these there is _.debounce:
debounce_.debounce(function, wait, [immediate])
Creates and returns a new debounced version of the passed function
which will postpone its execution until after wait milliseconds have
elapsed since the last time it was invoked. Useful for implementing
behavior that should only happen after the input has stopped arriving.
For example: rendering a preview of a Markdown comment, recalculating
a layout after the window has stopped being resized, and so on.
Pass true for the immediate argument to cause debounce to trigger the
function on the leading instead of the trailing edge of the wait
interval. Useful in circumstances like preventing accidental
double-clicks on a "submit" button from firing a second time.
In your case it's a matter of wrapping the handler function like this (I used 100ms for the timeout):
$(document).on('someEvent', _.debounce(function(event, data) {
if (var === 'something') {
console.log('Run');
}
}, 100));
Function "functionToBeCalled()" will be executed immediately, and every 0.4 seconds. If you want to call again that function after 0.4s and not every time replace setInterval with setTimeout.
var variable = "something";
$("#button").on('click', function(event, data) {
if ( variable === 'something') {
console.log('Run');
setTimeout(function(){
$("#button").trigger("click");
}, 1000)
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="button">Button</div>
You could use the current time:
var waitUntil = Date.now();
$(document).on('someEvent', function(event, data) {
if (Date.now() >= waitUntil) {
waitUntil = Date.now() + 5000 // 5 seconds wait from now
console.log('Run');
}
});
Here is a fiddle which uses a button click as the event, and gives feed-back on-screen about whether the event is treated or not.
Here's a neat little function that might help:
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
}

Miss-understanding or Miss-use of JavaScript's SetTimeout function

I was trying to make a function execute 4 times a second, however I know this won't be 100% precise, because it doesn't take the execution time of the function into consideration, however I managed to have a completely different result than expected.
CODE:
var ClassObject = {
myInteger: 0,
running: false,
paint: function() {
this.myInteger++;
console.log(this.myInteger);
if(this.running) {
setTimeout(this.paint(), 250); // 250ms wait
}
},
start: function() {
this.running = true;
this.paint();
},
stop: function() {
this.running = false;
}
}
ClassObject.start();
However, the result was something rediculous, the loop was running so fast my computer nearly crashed, and so much garbage was being created I couldn't escape hard-killing the process once it reached 80% CPU usage and almost 6GB of RAM. Over about 2 seconds of execution the console was printing values of over 13k. Shouldn't that be sitting around 8?
Not exactly sure what I did wrong.
Here's a JSFiddle: http://jsfiddle.net/3Lq9t1by/ (Warning: May freeze your browser!)
You're invoking paint with the () instead of passing a reference to it into the setTimeout. You'll probably also need to bind it or something as you're using this, which will be lost when the setTimeout invokes it
function () {
this.myInteger++;
console.log(this.myInteger);
if(this.running) {
setTimeout(this.paint.bind(this), 250); // 250ms wait
}
}
Please note however that this will create a new instance of the function each time, it may be more efficient to define another function inside paint which actually does the loop as you can bind it just the once
function () {
var looper = function () {
this.myInteger++;
console.log(this.myInteger);
if(this.running) {
setTimeout(looper, 250);
}
}.bind(this);
looper();
}

How to clear a javascript timeout thats set within a function

I have a recursive type function in Javascript that runs like this:
function loadThumb(thumb) {
rotate=setTimeout(function() {
loadThumb(next);
}, delay);
}
Note: I've simplified the function to make it easier to read.
I have "a" tags called like this
Load thumb 3
However, they don't clearout the timer, the timer continues to cycle through the function irregardless of the clearTimeout() being called.
Any ideas why? I think it might have something to do with a scope problem or something like that.
Yeah, you need to make rotate a global variable. Simply declare it outside the function like so:
var rotate;
var delay = 1000;
function loadThumb(thumb) {
alert("loading thumb: " + thumb);
rotate = setTimeout(function() {
loadThumb(thumb + 1);
}, delay);
}
Also, you need to make sure you clear the timeout before you call loadThumb. Otherwise you'll clear the timer you just started.
Load thumb 3
fiddle: http://jsfiddle.net/63FUD/
it may be the issue of scope so make rotate as global variable and call clearTimeout(rotate);
refer clearTimeout() example
It may be a scoping issue if you are not declaring rotate externally.
Try this:
var rotate = 0;
function loadThumb(thumb) {
rotate=setTimeout(function() {
loadThumb(next);
}, delay);
}
Return false on the link
Since you are not using var rotate, it should not be a scoping issue since rotate would be in the window scope. Can you show the complete code?
It is considered poor coding to inline the script - you should attach the event handler onload of the page
Also you should not have the setTimeout inside a function that might be called for one image
Try this:
var rotate,next=1;
function loadThumb(thumb) {
if (thumb) ... use thumb
else ... use next
}
function slide() {
rotate=setInterval(function() {
loadThumb();
next++;
if (next>=images.length) next=0;
}, delay);
}
window.onload=function() {
var links = document.getElementsByTagName("a");
if (links[i].className==="thumbLink") {
links[i].onclick=function() {
var idx = this.id.replace("link","");
loadThumb(idx);
clearInterval(rotate);
return false;
}
}
document.getElementById("start").onclick=function() {
slide();
return false;
}
document.getElementById("stop").onclick=function() {
clearInterval(rotate);
return false;
}
slide();
}
assuming
Start
Stop
Show 1
Show 2
Show 3
If you have to manage multiple timeouts, you can use an object in the global scope and some custom methods to create and remove your timeouts. To access the methods you can either put the calls in the onclick handler of your links (like in the example), or use a library like jQuery to bind them.
<script type="text/javascript">
var timeouts = timeouts || {};
function createTimeout(name, milliseconds, callback) {
timeouts.name = setTimeout(callback, milliseconds);
}
function removeTimeout(name) {
if (typeof(timeouts.name) !== undefined) {
clearTimeout(timeouts.name);
timeouts.name = undefined;
}
}
createTimeout('foo', 5000, function() {
alert('timeout')
});
</script>
i have also posted an example on jsFiddle http://jsfiddle.net/AGpzs/
I'm not sure what exactly you are doing, because as far as I can see you didn't post all the code, but this looks better for me:
function loadThumb(thumb) {
return setTimeout(function() {
loadThumb(next);
}, delay);
}
and then:
Load thumb 3

Categories