I have a number of different form elements. When any of the form elements have their value changed, I need to make an ajax call 500ms after the change.
However, should another form element have its value changed then I would like to reset the timer to 500ms, thus avoiding multiple Ajax requests for a series of changes that happen within 500ms of each other.
Is there a JavaScript or jQuery solution to this requirement?
Here's some code that demonstrates the principles you're looking for:
// Keeps track of the timer id in a scope that is outside of the event function
// The variable will remain in memory and available to the next event call
var myTimer;
// Detect changes on keyup.
$('.textbox').on('keyup', function () {
console.log('keyup');
setMyTimer(500);
});
// Detect on change.
$('select').on('change', function () {
console.log('change');
setMyTimer(1000);
});
function setMyTimer(timerDelay) {
// if myTimer has a value, then we should clear the timer. This stops us
// from queuing multiple timers
if (myTimer) {
console.log('clear tiemout');
clearTimeout(myTimer);
}
// Set the timer. It will be cleared if there is another handled 'keyup'
// event sooner than the timerDelay parameter
myTimer = setTimeout(function () {
console.log('Ajax stuff');
// ajax stuff
}, timerDelay);
};
Remove the console.log code before using in production.
See this working demonstration:
http://jsfiddle.net/cC6Dq/5/
Related
I tried the answer here: Resetting a setTimeout, but it doesn't seem to be working for me.
I'm building a catalog viewer using Owl Carousel. I have a function set to go off on the afterMove event handler that shows what page the user is on. It displays the page counter and then sets a timeout to have it fadeout after 1 second. Probably is lots of people go through pages faster than once per second. So, I need to reset the timeout if the function gets called again.
function showCounter(){
var $counter = $('#counter');
//clear timeout
window.clearTimeout(timeout);
//Display counter
$counter.show();
//set timeout
var timeout = window.setTimeout(function(){
$counter.fadeOut(500);
}, 1000);
}
But window.clearTimeout(timeout) doesn't seem to be working and I'm not sure why
Thanks
var timeout inside the function makes timeout local to the function; thus, every time you call showCounter, timeout is undefined. Move the variable declaration out of the function:
var timeout;
function showCounter() {
// ...
timeout = // NO VAR! ...
// ...
}
I use Ajax load icons all the time for when I do ajax requests.
I want to know if it's possible to accomplish the same thing with just regular javascript code. For example:
$('button').on('click', function(){
showLoadingBar();
longProcess();
hideLoadingBar();
});
longProcess() is just a function that can take 1-3 seconds to process (it does a lot of calculations and manipulates the DOM but doesn't do any ajax requests).
Right now, the browser halts/freezes during these 1-3 seconds, so what I would rather do is show a loading icon during this time. Is this possible? The code I have above pretty much ignores showLoadingBar().
The DOM won't be updated until the current Javascript process ends, so you can't do it just like that. You can however use setTimeout to get around that:
showLoadingBar();
setTimeout(function() {longProcess(); hideLoadingBar(); }, 1);
What happens above, in case it isn't clear, is that showLoadingBar is executed, then a timeout is set up. The current process will then end and allow the DOM to update with the loading bar, before the timeout is invoked, very shortly after. The handler executes your heavy function and when it's done, hides the loading bar again.
The following will give you control over the action on click. What this means is you can disable the clicking ability till it has finished running. But also i've including setTimeout which returns control to the browser (thus removing that annoying "lockup" feeling) and in the timeout function we preform our long process then re-enable the button! VIOLA!
function startProc() {
var $this = $(this);
$this.off("click", startProc);
showLoadingBar();
setTimeout(function() {
longProcess();
hideLoadingBar();
$('button').on('click', startProc);
});
}
$('button').on('click', startProc);
Dude,
Use the .bind method, which in this case is performed so:
$('button').bind('click', function(){
showLoadingBar();
longProcess();
hideLoadingBar();
});
When somebody clicks my checkboxes, from a long list of checkboxes, I want to show the number of selected checkboxes in a little popup element. My problem is, the little popup element should disappear 5 seconds after the last click, which is OK for one checkbox being clicked, but if I quickly check 5 boxes, the timer is still set on the first box, resulting in the popup element disappearing too quickly.
As you can see in my function, I've tried using the clearTimeout(timeoutName) function but have experienced some troubles applying it. The console log states that the clearTimeout(timeoutName) is undefined, which I can understand: the setTimeout hasn't even started yet.
How can I check that the timer exists before I clear it? Or is this really not the best method? When a checkbox is checked (this function runs) there could be a timer running but sometimes there could not be.
$('.name_boxes').live('click', function() {
var checked_count = $('.name_boxes:checked').length;
// other stuff
clearTimeout(hide_checked_count_timer); // if exists????????
$(".checked_count").hide();
$(".checked_count").text(checked_count+" names selected");
$(".checked_count").show();
hide_checked_count_timer = setTimeout(function() {
$(".checked_count").hide();
},5000);
});
Any help gratefully received...
Just declare the timer variable outside the click handler:
var hide_checked_count_timer;
$('.name_boxes').live('click', function() {
var checked_count = $('.name_boxes:checked').length;
// other stuff
clearTimeout(hide_checked_count_timer); // if exists????????
$(".checked_count").hide();
$(".checked_count").text(checked_count+" names selected");
$(".checked_count").show();
hide_checked_count_timer = setTimeout(function() {
$(".checked_count").hide();
},5000);
});
http://jsfiddle.net/kkhRE/
Considering .live has been deprecated, you should be delegating the event with .on instead:
// Bind to an ancestor. Here I'm using document because it an
// ancestor of everything, but a more specific ancestor
// would be preferred.
$(document).on('click', '.name_boxes', function() {
// ...
});
Q. The console log states that the clearTimeout(timeoutName) is undefined, which I can understand: the setTimeout hasn't even started yet.
A. The clearTimeout() function's return value is undefined regardless of whether there was a timeout to be cleared. It doesn't have a concept of "success" that can be tested. If there is a queued timeout associated with the id you pass then it will be cleared, otherwise nothing happens.
Q. How can I check that the timer exists before I clear it?
You can't, at least not in the sense of there being some registry of outstanding timeouts that you can query. As you already know, the .setTimeout() function returns an id for the timeout just queued, and you can use that id to clear it before it runs, but there is no way to test whether it has already been run. The id is just a number so the variable that you saved it in will continue to hold that number even after the timeout has either run or been cleared.
It does no harm at all to call clearTimeout() with an id for a timeout that already ran - basically if the timeout for that id is in the queue it will be cleared otherwise nothing will happen.
The easiest way to test "Is there an outstanding timeout that hasn't run yet" is to set the variable holding the timerid to null when the timeout runs, i.e., within the function you queued:
var timerid = setTimout(function() {
timerid = null;
// other operations here as needed
}, 5000);
// in some other code somewhere
if (timerid != null) {
// timer hasn't run yet
} else {
// timer has run
}
The variable you save the timerid in needs to be in a scope that can be accessed both where you set it and where you test it, i.e., don't declare it as a local variable within an event handler.
You can use the power of short-circuit operators
hide_checked_count_timer && clearTimeout(hide_checked_count_timer);
The right-hand statement will only run if the left-hand variable is not undefined.
to check if it exists use;
if (typeof timerid == 'undefined')
{
//timer has not been set so create it
timerid = setTimeout(function(){ var something = true;}, 5000);
}
I want to call a js function when there is no activity from user on the web page for specified amount of time. If there is activity from user then reset timeout. I tried to search but couldn't find anything in particular. I am familiar with setTimeout() and clearTimeout() and how they work. What I am looking for is where/how to monitor for user activity. Is there any event in which I can set and clear timer?
Thank you.
Edit #1:
This webpage has one input text box & one button. It's kind of regular chat page. When I say no user activity, I mean that the user has not typed anything in text box or has not pressed any button for specified amount of time. And one more thing that it is targeted for touch based smartphone devices.
Edit #2:
Thank you everyone for suggestions. I've implemented solution based on more than one answers provided. So I will give upvote to all answers that I've found helpful instead of accepting one as answer.
// Using jQuery (but could use pure JS with cross-browser event handlers):
var idleSeconds = 30;
$(function(){
var idleTimer;
function resetTimer(){
clearTimeout(idleTimer);
idleTimer = setTimeout(whenUserIdle,idleSeconds*1000);
}
$(document.body).bind('mousemove keydown click',resetTimer); //space separated events list that we want to monitor
resetTimer(); // Start the timer when the page loads
});
function whenUserIdle(){
//...
}
Edit: Not using jQuery for whatever reason? Here's some (untested) code that should be cross-browser clean (to a point; doesn't work on IE5 Mac, for example ;):
attachEvent(window,'load',function(){
var idleSeconds = 30;
var idleTimer;
function resetTimer(){
clearTimeout(idleTimer);
idleTimer = setTimeout(whenUserIdle,idleSeconds*1000);
}
attachEvent(document.body,'mousemove',resetTimer);
attachEvent(document.body,'keydown',resetTimer);
attachEvent(document.body,'click',resetTimer);
resetTimer(); // Start the timer when the page loads
});
function whenUserIdle(){
//...
}
function attachEvent(obj,evt,fnc,useCapture){
if (obj.addEventListener){
obj.addEventListener(evt,fnc,!!useCapture);
return true;
} else if (obj.attachEvent){
return obj.attachEvent("on"+evt,fnc);
}
}
This calls for a debouncer:
function debounce(callback, timeout, _this) {
var timer;
return function(e) {
var _that = this;
if (timer)
clearTimeout(timer);
timer = setTimeout(function() {
callback.call(_this || _that, e);
}, timeout);
}
}
Used like this:
// we'll attach the function created by "debounce" to each of the target
// user input events; this function only fires once 2 seconds have passed
// with no additional input; it can be attached to any number of desired
// events
var userAction = debounce(function(e) {
console.log("silence");
}, 2000);
document.addEventListener("mousemove", userAction, false);
document.addEventListener("click", userAction, false);
document.addEventListener("scroll", userAction, false);
The first user action (mousemove, click, or scroll) kicks off a function (attached to a timer) that resets each time another user action occurs. The primary callback does not fire until the specified amount of time has passed with no actions.
Note that no global flags or timeout variables are needed. The global scope receives only your debounced callback. Beware of solutions that require maintenance of global state; they're going to be difficult to reason about in the context of a larger application.
Note also that this solution is entirely general. Beware of solutions that apply only to your extremely narrow use case.
Most JavaScript events bubble, so you could do something like the following:
Come up with a list of all the events you'd consider to be "activity from the user" (e.g., click, mousemove, keydown, etc.)
Attach one function as an event listener for all of those events to document (or maybe document.body for some of them; I can't remember if that's an issue or not).
When the listener is triggered, have it reset the timer with clearTimeout/setTimeout
So you'd end up with something like this:
var events = ['click', 'mousemove', 'keydown'],
i = events.length,
timer,
delay = 10000,
logout = function () {
// do whatever it is you want to do
// after a period of inactivity
},
reset = function () {
clearTimeout(timer);
timer = setTimeout(logout, 10000);
};
while (i) {
i -= 1;
document.addEventListener(events[i], reset, false);
}
reset();
Note that there are some issues you'd have to work out with the above code:
It's not cross-browser compatible. It only uses addEventListener, so it won't work in IE6-8
It pollutes the global namespace. It creates a lot of excess variables that might conflict with other scripts.
It's more to give you an idea of what you could do.
And now there are four other answers, but I've already typed it all up, so there :P
You want to monitor events like mousemove, keypress, keydown, and/or click at the document level.
Edit: This being a smartphone app changes what events you want to listen for. Given your textbox and button requirements, I'd listen to oninput and then add the resetTimeout() call to the click handler for your button.
var inactivityTimeout = 0;
function resetTimeout() {
clearTimeout(inactivityTimeout);
inactivityTimeout = setTimeout(inactive, 300000);
}
function inactive() {
...
}
document.getElementById("chatInput").oninput = resetTimeout;
Something like this:
function onInactive(ms, cb){
var wait = setTimeout(cb, ms);
// Bind all events you consider as activity
// Note that binding this way overrides any previous events bound the same wa
// So if you already have events bound to document, use AddEventListener and AttachEvent instead
document.onmousemove = document.mousedown = document.mouseup = document.onkeydown = document.onkeyup = document.focus = function(){
clearTimeout(wait);
wait = setTimeout(cb, ms);
};
}
IE: http://jsfiddle.net/acNfy/
Activity in the bottom right frame will delay the callback.
I'm using a nifty little 'delay' method for this that I found in this thread
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
use like
delay(function(){ doSomethingWhenNoInputFor400ms(); },400);
Also, take a look at jQuery idleTimer plugin from Paul Irish (jquery.idle-timer.js). It was based on Nicholas C. Zakas' Detecting if the user is idle with JavaScript and YUI 3 article (idle-timer.js).
It looks at similar events to the other answers, plus a few more.
events = 'mousemove keydown DOMMouseScroll mousewheel mousedown touchstart touchmove';
// activity is one of these events
I want to run some code on all my treeView nodes depending on a value returned from the database and repeat this until a certain value is returned.
I was thinking that:
Give all my tree nodes the same css class so I can access them from JQuery
have a timer in my JQuery function that used ajax to go to the database, when a certain value is returned then stop the timer
Two questions here. How can I make my function run for each of the nodes and how do I do a timer in JavaScript, so:
$(function(){
$('cssClassOfAllMyNodes').WhatFunctionToCallHere?((){
//How do I do Timer functionality in JavaScript?
ForEachTimeInterval
{
//use Ajax to go to database and retrieve a value
AjaxCallBackFunction(result)
{
if (result = 1)
//How to stop the timer here?
}
}
});
});
Hope i'm clear. Thanks a lot
thanks a lot for the answer. And i would like you to comment on the design.
Bascially what i'm trying to acheive is a Windows Wokflow type functionality where each node in my tree updates its image depending on its status, where its status is got from querying the database with a key unique to the tree node. I'm open to ideas on other ways to implement this if you have any. thanks again
Without commenting on your design you can refer to these
$.each()
setTimeout() or setInterval()
You can do:
$(function(){
$('cssClassOfAllMyNodes').each(function (){
// Do something with "this" - "this" refers to current node.
});
});
Te proper way to handle timers in JS is to have a reference to each timeout or interval and then clearing them out.
The difference between them is:
The timeout will only run once, unless stopped before;
The interval will run indefinitely, until stopped.
So you can do something like:
var delay = 2000; // miliseconds
var timer = setTimeout("functionToBeCalled", delay);
clearTimeout(timer); // whenever you need.
Please note you can pass a string to setTimeout (same with setInterval) with the name of the function to be called. Or you could pass a reference to the function itself:
var callback = function () { alert(1); };
var timer = setTimeout(callback, delay);
Be sure not to set an Interval for AJAX requests, because you response might be delayed and successive calls to the server could eventually overlap.
Instead, you should call setTimeout and when the answer arrives then call setTimeout again.