Update (May 2015): The following article deals with the nowadays well know JavaScript scope behavior and it’s much better explained/solved elsewhere.
While working on real time web chat application I run into problems with JavaScript’s setTimeout function. I would like to write down what I learned. It may save you some time, next time you’ll need to use this function.
Shortly about setTimeout
setTimeout is a simple JavaScript function used to repeatedly call some function after a specified amount of time. It takes two required parameters and an unspecified number of optional ones. Like this:
setTimeout(functionToCall, time, param1, param2, ....);
functionToCall is the name of the function which will be called after time millisecond. You either use reference like in the example above or a string representing a call to the function:
setTimeout('functionToCall()', time, ...)
The optional parameters can be used to pass any number of parameters to our functionToCall.
Where’s the catch?
Everything works as expected until you try to call a method inside your ‘class’ (there are no real classes in JavaScript). Something like this won’t work:
setTimeout(this.methodToCall, time);
Passing a string representation instead of reference doesn’t work either.
The solution
I found the solution after a while searching in Google Code Search. The above example needs to be rewritten like this:
setTimeout(function(thisObj) { thisObj.methodToCall(); }, time, this);
Here we are passing reference to our class as an optional parameter of the setTimeout function. In the called function we catch it and suddenly we can use it to call our method. I have no idea why such a simple thing needs to be so complicated but it only demonstrates that you need to learn a few tricks when you want to do real OOP in JavaScript.
Oh btw,
your above setTimeout() solution only works for Firefox. For IE, it fails since IE’s version of setTimeout doesn’t allow you to pass in extra argument.
I’ve found the solution for IE and the link is http://alexle.net/archives/169
Cheers!
Alex
Thank you very much for that link Alex. That article is great.
You’re right that this solution does not work in IE, I’ll rewrite the article a bit. Now that I am on Ubuntu I sometimes forget THE browser 🙂
You can get around all this by using the fact that Javascript variables are global unless defined inside a function. Simply define the variables you want to manipulate and pass around before any function definitions, then change them inside your function(s).
E.G.
// REQUIRED GLOBAL VARS
var timer = 2000;
var counter1 = 0;
var images1 = new Array();
images1[0] = ‘icons/andy.jpg’;
images1[1] = ‘icons/condi.jpg’;
images1[2] = ‘icons/hotzone.jpg’;
var numImages1 = images1.length;
var counter2 = 0;
var images2 = new Array();
images2[0] = ‘icons/beeftenderloin.jpg’;
images2[1] = ‘icons/choosingwine.jpg’;
images2[2] = ‘icons/datenightsalmon.jpg’;
var numImages2 = images2.length;
function switchImages(){
if (document.getElementById(“img1”)){
document.getElementById(“img1”).src = images1[counter1];
counter1++;
if(counter1 >= numImages1){
counter1 = 0;
}
}
if (document.getElementById(“img2”)){
document.getElementById(“img2”).src = images2[counter2];
counter2++;
if(counter2 >= numImages2){
counter2 = 0;
}
}
setTimeout(“switchImages()”, timer);
}
yes, that’s a way to do it. thanks Pat.
Scope is crazy in Javascript. See the following. Watch the variable appt_extrainfo_div. This is almost sudo code as I’ve replaces some of my own functions with their basic function. Specifically somediv.onclick = function(){} is something I have a function for that handles various browser differences. Just don’t copy/paste and expect to work.
Being a C programmer, I can’t say this jives with “my” way of doing things, but it is how JS does it.
Tested in IE7
function PopupBoxShow(div,x,y)
{
// set div’s style to show up and position somewhere
}
function MyFunc()
{
var appt_extrainfo_div=document.createElement(‘div’);
// do some stuff in the div. make it hidden for now.
// Clicking another div makes this extrainfo div appear after 2 seconds.
someotherdiv.onclick = function () {
var x=window.event.clientX; // for non-IE, you should do something better here.
var y=window.event.clientY;
global_popup_timer = setTimeout( function () {
//
// YES! I have access to appt_extrainfo_div because
// my function was declared in the function it exists in.
// Even though the function returns before the “click”
// happens, the reference to this variable is allowed
//
PopupBoxShow(appt_extrainfo_div, x, y);
}, 2000);
}
}
This will also work (the solution by Alex Le is a bit long-winded and uses ‘eval()’):
thisObj = this;
setTimeout(function() { thisObj.methodToCall(); }, time);
If you are using the prototype javascript, you can take advantage of its general purpose early-binding function, bind (see http://alternateidea.com/blog/articles/2007/7/18/javascript-scope-and-binding).
The solution using prototype’s bind function is:
setTimeout(this.methodToCall.bind(this), time);
your above setTimeout() solution only works for Firefox. For IE, it fails since IE’s version of setTimeout doesn’t allow you to pass in extra argume.
http://www.sumanth.in
No solution is working for me in IE:(. I want to close the window after submitting the form. But before closing the window i want to wait for few seconds. this is how i am doing it…
…………..
formId.submit();
thisObj = this;
setTimeout(function() { hisObj.closeWindow();},3000);
….
function closeWindow(){
window.opener.focus();
window.close();
}
Please help me out….
chuck’s approach is almost perfect. I rather use:
thisObj = this;
setTimeout(function() { thisObj.methodToCall.call(thisObj); }, time);
which enusure you that scope is correctly passed.
Thank you for your post!
Even David Flanagan’s book example code fails in Firefox. I used Firebug to set breakpoints, but the function was never called.
example 14.6
var WastedTime = {
start: new Date(), // Remember the time we started
displayElapsedTime: function() {
var now = new Date(); // What time is it now
// compute elapsed minutes
var elapsed = Math.round((now – WastedTime.start)/60000);
// And try to display this in the status bar
window.defaultStatus = “You have wasted ” + elapsed + ” minutes.”;
setTimeout(WastedTime.displayElapsedTime, 1000);
}
}
// Update the status line every minute
setTimeout(WastedTime.displayElapsedTime, 1000);
How about setting more than 1 timeout (for different function)?
Let say I have function A that give warning about session ending soon ask if user wants to refresh the session, and function B that stop a session. I’ll need to set timeout for function A 30 seconds before function B.
I’ve tried
timer1 = window.setTimeout(‘A()’, 30000);
timer2 = window.setTimeout(‘B()’, 60000);
This works well in FireFox, but in IE, it don’t get to execute function B if i didn’t click ok on alert() or reply the confirm() in function A.
Do you have any idea?
The following works in IE 7, but not in Firefox 3.0.1. Please assist:
var c=0;
function DisableClick()
{
var objName = ‘btnPost’;
document.getElementById(objName).disabled=true;
c=c+1;
msg = ‘Please Wait…(‘+ c +’)!’;
document.getElementById(objName).value= msg;
var t=setTimeout(‘DisableClick()’,5000);
}
I think I figured the above problem out. FireFox does not like the recursive call to setTimeout. The below javascript disable button code works in both IE and FF, but use an HTML input control to runat server, like this:
var objName = ‘btnPost’;
function DisableClick()
{
document.getElementById(objName).disabled = true;
setTimeout(‘EnableClick()’, 5000);
}
function EnableClick()
{
document.getElementById(objName).disabled = false;
}
I have to add one more comment. To make the code above work in Firefox I needed to remove several Microsoft .Net 2.0 regular expression controls. They seem to intefere with Firefox postback javascript. I’m guessing this is the case with all Microsoft client side validation controls.
i have the problem of time dealay in mozila 2.0 of 25 to 75 ms per seconds so in 10 minute i got the 30 to 40 seconds difference how can i solve it
Thanks! I really helps.
It’s articles like this that make me help someone else. From author to commenter: excellent job.
Try this solution:
var toDelay = function() { animate( type, numb, _this ); };
setTimeout( toDelay, flakes[numb].timeout );
Try this solution:
function animate( type, numb, _this ){
var toDelay = function() { animate( type, numb, _this ); };
setTimeout( toDelay, flakes[numb].timeout );
}
link: http://lejnieks.com/2008/08/passing-arguments-to-javascripts-settimeout-method-using-closures/
works in IE and FF
Thanks!!
Very helpful!!
What Chuck said. I was even able to omit the first line/step from his solution, leaving simply:
setTimeout(function() { this.MethodToCall(); }, time);
which worked for me in IE 8 but, alas, not in FF 3.0.10 (for which I had to revert to his original solution).
Here’s a full solution:
function thisReferencingCallback(instance, method) {
return function() {
return method.apply(instance, arguments);
}
}
setTimeout(thisReferencingCallback(this, someMethod), 50);
hello,
i use;
function method(){
if(document.getelementbyid(id)<200){
document.getelementbyid(id).style.left=document.getelementbyid(id).offsetLeft+10;//that line
setTimeout(method(),1000);
}
}
to see a slow slide effect.
but i just see my div on 200px right of the left edge of browser :S
where am i wrong?
should not it shows the effect of single “that line”?
thanks in advance
ok, it is solved thanks