Put your message here! Contact me for more information
 
 








 


I was working on some javascript stuff for a project and ran into this problem with setInterval(): for IE 6, calling setInterval() to an object’s method results in the wrong scope. In the object’s method, the alfamous **this** keyword now refers to **window**, instead of the conventional “this” as in “**this object**”. So let me give you some more details on this and a free, amazingly simple solution to this head-ache.

**SPOILER:**\\ the solution uses eval(). if you are allergic to eval(), don’t take my advice.\\ **END SPOILER**

===== The code =====
Let’s look at some code and we’ll have a little discusssion. Here’s the psuedo-code for a fictional TimeTicker object, which is supposed to automatically update a “clock” on the screen every second.


/* A. Constructor */
function TimeTicker()
{
/* A.1 - populate internal variable here, like hour, minute, second, etc. */
...

/* A.2 - now we set the timer to periodically update the time value every 999 miliseconds */
this.timer = setInterval( this.updateTime, 999 );
}

/* B. This function is to update the time value somewhere on the screen */
TimeTicker.prototype.updateTime = function ()
{
/* B.1 - compute new value */
...
this.hour = new_hour_value;
this.minute = new_minute_value;
this.second = new_second_value;
...

/* B.2 - now update the screen */
...
}

===== The problem =====
The above psuedo code will sadly **FAIL** in both IE and FF (not because it’s only psuedo-code!), as the ‘’setInterval()” invocation has **totally changed the scope** of the polled function. First of all, let’s look at ”**A.2**” from above. Calling ”this” within the ‘’setInterval()” will change the reference of ”this” to some other object (which I think is ”this” now refers to ”window”.) Hence calling ‘’setInterval( this.updateTime, 999 )” fails in both Firefox and IE 6, as it means that the ‘’setInterval()” will poll the ”window.updateTime()” function every 999 ms. That’s not what we want, and ”window” object could care less about our ”updateTime()” since we suddenly found ourselves in the wrong scope.

===== The Firefox’s Solution =====
For firefox, the fix is a small rewrite of how we would call the polled-function, updateTime(). The setInterval() function in Firefox is improved and we can now pass an extra parameter to it, and that’s exactly what we will do. We pass in the current object’s reference as ”this” to the a proxy function, which in turn makes a call to the updateTime(). I found this solution through [[http://www.klevo.sk/javascript/javascripts-settimeout-and-how-to-use-it-with-your-methods/|Klevo’s blog]], a web developer from Slovakia.

We will have to rewrite ”**A.2**” as follows:


/* A.2 */
this.timer = setInterval ( function( that ) { that.updateTime(); }, 999, this );

What we just did is we create a proxy function, whose only parameter is ”that”, a pointer to another object. Within this proxy function, we can make invoke all of “that”’s methods! Basically we migrate the scope of ”this” as a reference to the current ”TimerTicker” object into the polled proxy function via a pass-by-reference ”that” parameter. Notice the extra 3rd parameter of ‘’setInterval()”: we are passing ”this”, in this case, a reference to the current ”TimeTicker” object, as the only parameter of the newly created proxy function. Since now ”that” is equivalent to ”this”, we can call all methods of TimeTicker object! Beautiful!

===== The Internet Explorer’s Problem =====
While setInterval() in Firefox provide us a way to pass in extra parameters, IE’s version of ‘’setInterval()” is less flexible (and it also did drive me nuts!) Anyway, let’s talk about the mechanism of IE’s ‘’setInterval()”. According to [[http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/setinterval.asp|MSDN’s manual on setInterval()]], the syntax of setInterval() is

”iTimerID” = ”window”.**setInterval(** ”vCode”, ”iMilliSeconds” [, sLanguage])

with ”vCode” is either a reference pointer or a “string that indicates the code to be executed when the specified interval has elapsed”. Since there’s no way we can pass an extra parameter to the polled-function, we cannot pass ”this” as a 3rd parameter of setInterval() as what we just did for Firefox.

If we rewrite the code as this.timer = setInterval ( this.updateTime(), 999);then 2 things are gonna happen: first,” this.updateTime()” is executed right away. It’s equivalent to a direct call to the ”updateTime()” function of the current object, ”TimeTicker”. As we are still in the scope of the current function, hence, ”this” refers to the current ”TimeTicker” object. Secondly, after the 999ms has elapsed, ‘’setInterval()”" will invoke ”this.updateTime()”, and now ”this” refers to ”window” as we are now out of the scope of the ”TimeTicker” object. And of course, ”window.updateTime()” is wrong. So what you see is that your script will work the first time (when this.updateTime() is executed in the correct scope), and then everything fails (when this.updateTime() is excuted by setInterval() in the wrong scope).

And If we write this.timer = setInterval ( this.updateTime, 999);(Notice the missing parenthesis). This will also fails as when this.updateTime is invoked, we are already in the wrong scope, the scope of ”window” object!

On the other hand, if we writethis.timer = setInterval ("this.updateTime()", 999);we are still in the wrong scope when setInterval() invoke the updateTime() method. ”this” still refers to ”window” when the updateTime() function is executed. Notice that we can include the parenthesis “( )” (and potentially other pass-by-value parameters since the method is refered to as a string and it’s not executed right away, but only when being invoiked by ‘’setInterval()”). However, we can’t pass a pointer to the current object in a string!

We’d like to pass a pass-by-reference pointer object to point to the current ”TimeTicker” object but no matter what we do, we cannot seem to do so. But be hold! Our salvation comes from the use of a glorifying global variable, and of course, a dash of ”eval()”, just enough to tame Internet Explorer.

===== The Internet Explorer’s Solution =====
I’d like to include the code for IE here, just read over and we will discuss later.


/* C. global scope object */
var globalScope = new Array();

/* A. Constructor */
function TimeTicker()
{
/* A.0 - An extra unique identifier for the current object */
this.uniqueId = some_text_or_numers;

/* A.1 - populate internal variable here, like hour, minute, second, etc. */
...

/* A.3 - setInterval Fix */
/* A.3.1 - for IE */
if( document.all )
{
/* A.3.1.1 - make a reference to the current object and saved in the global scope array
* to use later to correct the scope.
*/
globalScope[ this.uniqueId ] = this;
setInterval( 'ieIntervalHandler("' + this.uniqueId + '","updateTime")', 999 );
}
else
{
/* A.3.2 - Mozilla */
this.timer = setInterval ( function( that ) { that.updateTime(); }, 999, this );
}
}

And we add an extra function, ”ieIntervalHandler( **id**, **strFunc** )” to the script.

/* D. - Special IE setInterval handler function with scope corrected */
function ieIntervalHandler( id, strFunc )
{
/* D.1 - correct the scope then make the call */
var scope = globalScope[id];
eval( "scope." + strFunc + "()" );
}

All set? Here comes the explanations: First of all, we added a global associative array named globalScope (”**C**”). This associative array is used to store reference to a particle object based on that object’s unique identifier. Confused? Not a problem, just keep on reading.

Let’s read A.0, this is where we set the unique identifier for this ”TimeTicker” object. For this technique to work, each object **must** have a **unique identifier** since the unique identifier is used as the **key** of the global associative array.

Look at ”**A.3.1**”. Here we do a little browser-intelligence. If the browser doesn’t know ”document.all”, which is IE-specific, we use the improved version of ‘’setInterval()” as mentioned in the above Firefox’s solution section. If the browser is indeed IE, at ”**A.3.1.1**”, we store the reference to this ”TimeTicker” object as an element in the globalScope associative array. Then we cheerfully and confidently ‘’setInterval()” the ”ieIntervalHandler()” function (see ”**D.1**”). We do a pass-by-value the unique ID of the current ”TimeTicker” object as the first parameter of ”ieIntervalHandler()”, and ”updateTime” as the name of the method of the same ”TimeTicker” object.

Look at ”**D.1**”. Here we have a 2-line function. First, we get the reference to the particular TimeTicker object, which we already stored in the globalScope associative array back in ”**A.3.1.1**”. Since we are now in the scope of ieIntervalHandler(), our ‘’scope” variable is pointing to the ”TimeTicker” object. With the correct reference to the correct object, now we can make the call to the correct method with a quick, painless ”eval()”eval( "scope." + strFunc + "()" );

What did we just do? We use a global array to store the references so that later on we can get to the right object, even when ieIntervalHandler is invoked by ‘’setInterval()” and the scope now is all over the place — we could care less! Hence we now can solve the wrong scope issue within IE. And guess what, now we can have any object to poll a method of itself! How awesome is that?

===== Memory Issue =====
Since we need to keep polling a function, we need to be careful of not creating more and more objects or functions that the garbage collect will never be able to purge because of a hidden reference somewhere. If you do create a lot of objects that do self-polling using this global associative array technique, when destroying the object, you will probably have to set the element of the globalScope to ”**null**” also, so that there is no further reference to the destroyed object. Also, in ”**A.3.2**”, we do create a new function everytime we’d like to run the timer, this may be a potential memory leaking point on Firefox.

I’d like to leave the quest of battling memory-leak monster to somebody else, but just remeber to watch out for potential weak point where memory leak can happen, especially when your page will be opened for a prolonged preriod of time.

===== Keywords spam =====
This setInterval() and the scope problem is a pretty abstract and hard to describe problem. I just include some potential search keywords here so that other people can by chance find this page. Call it keyword spam, call it shameless-self advertising, I could care less, but if somebody find this helpful, then I call it success :)

setInterval scope loosing, setTimeout, this setInterval wrong scope, javascript setInterval scope, global associative array scope correction, object oriented setInterval, self polling, this polling scope error, internet explorer setInterval fail, firefox setInterval fail, setInterval object.

===== Final remarks =====
Thank you for reading! I hope this helps. And I’d like to wish you a …

Merry Christmas and Happy New Year!

4:13AM, Dec 23, 2006.


 

21 Responses to “setInterval() Scope problem & solutions for Firefox and Internet Explorer 6



1:31 pm
December 22, 2006
#10494

Awesome article. Too bad I haven’t found it sooner, would save me a lot of time banging my head against monitor :)




12:02 am
February 9, 2007
#16062

This is an ingenious solution. Thanks for your innovation!




Arindam Biswas
7:59 pm
February 16, 2007
#16555

Hi, Great Article. Was awake for over six hours overnight and finally hit the target with your solution.




Tim Cooijmans
8:27 am
March 2, 2007
#17595

Actually, the “function () { … }” construct is called a closure. You can lure local variables into closures, just not keywords like “this”. What you were trying to do could just as easily have been accomplished using the following blurb:

var obj = this;
setInterval(function () { obj.method(); }, 1000);

Closures are powerful that way.




Ben
11:26 am
May 30, 2007
#32818

Here’s my approach:

I have an inline function call in onmouseover on an element:

Then in my script:
function delayMenu(element) {
var e = element;
setTimeout(function() { showMenu(e); }, 1000);
}

(substitute setInterval for setTimeout and modify as necessary).




David Frenkiel
9:36 am
July 3, 2007
#42420

Hi Alex and all,

The solutions described here are nice but they don’t take full advantage of the mechanisms provided by the language.
Look up Function.apply(). This is a much cleaner way than using eval(). Extending Ben’s approach of using a closure, you can do the following:

function delayMenu(element) {
var oThis = this;
// assuming this code is executed in the scope of some object that you wish to preserve in the showMenu callback.

var e = element;
setTimeout(function() { oThis.showMenu.apply(oThis, [e]); }, 1000);
}

More info: http://msdn2.microsoft.com/en-us/library/4zc42wh1.aspx




David B
8:07 pm
August 2, 2007
#48169

Good Article, great comments guys. Thanks for the insight, I’ve been at odds end with calling my object methods inside setTimeout!




8:29 am
October 18, 2007
#67166

If you want, using a “pointer” (use an array in AS), you can pass the interval id, as a parameter, to the callback function thus solving any scope problem.

This way:

function DelayedTrace(txt) {
var Pointer=[];
var interval=setInterval(function(Ptr,txt) { clearInterval(Ptr[0]); trace(”OK”) },2000,Pointer,txt);
Pointer[0]=interval;
}

[ this function will write a trace of the parameter after 3 seconds and then clean up after herself, cleaning the interval no matter the scope]
DelayedTrace(”Hello”);




9:15 am
December 11, 2007
#87278

Hi Alex

I really appreciate what you have shown us here. Great work!




Aaron Michaux
7:09 am
February 5, 2008
#111750

Excellent description of the problem. I found the simplest solution uses the closure feature of the language as follows:

var foo = this;
setInterval(function() { foo.bar(); }, 900);

Closure means that the local variable ‘foo’ will persist because it is referenced by that anonymous… function() {foo.bar();}




9:17 am
March 26, 2008
#134437

After hitting my head against a similar wall and coming to a similar conclusion I couldn’t accept it. I’m glad I didn’t.

base on Tim Cooijmans solution I wrote a little state machine function. The timing code on it boils down to this.

this.myFunction = function() {
var delay = 100;
var obj = this;
setTimeout(function() {obj.myFunction()}, delay);
}




Sepehr Lajevardi
3:07 pm
June 3, 2008
#164171

Many thanks Alex.
I was absolutely disappointed about IE.




[…] - Heute, 20:04 Tach, Google-Suchstring "ie setinterval problem", Platz 1: Alex Le’s Blog » Blog Archive » setInterval() Scope problem & solutions for Firefox and Internet… Gruß, […]




Vik
9:35 pm
August 13, 2008
#197995

Thank you very much - my initial solution to the problem was a global array of objects - but using closure is much cleaner =)




Mike
12:51 pm
October 29, 2008
#225820

Thanks for all this info! This helped me a lot, but I am still having problems. How does clearInterval fit into all this. Using Tim’s code about javascript closures, I was able to get the setInterval working, but it would not end! I would call clearInterval(this.timer) (this.timer being the return from setInterval, of course) from a different function, and it would not work. How can you hook the timer variable into the scope so that it will cause the interval to stop running once it is cleared?




Alexander Reintzsch
6:51 am
November 13, 2008
#229133

Sorry for posting twice,
but actually you don’t need the ieIntervalHandler - method anymore.
use

setInterval( ‘globalScope[”‘ + this.uniqueId + ‘”][”updateTime”]();’, 999 );

instead.

The example from above w/o comments.

var globalScope = {currentId:0,getRandomId:function(){return this.currentId++;}};

function TimeTicker()
{
this.uniqueId = globalScope.getRandomId();
this.countdown = 0;
if( document.all ){
globalScope[ this.uniqueId ] = this;
setInterval( ‘globalScope[”‘ + this.uniqueId + ‘”][”updateTime”]();’, 999 );
} else {
this.timer = setInterval ( function( that ) { that.updateTime(); }, 999, this );
}
}
TimeTicker.prototype.updateTime = function()
{
window.status = this.uniqueId + ” : ” + this.countdown++;
}




Alexander Reintzsch
6:56 am
November 13, 2008
#229136

function TimeTicker()
{
this.countdown = 0;
if( document.all ){
if (typeof globalScope != “object”)
globalScope = {currentId:0,getRandId:function(){return this.currentId++;}};
globalScope[ this.uniqueId = globalScope.getRandId() ] = this;
setInterval( ‘globalScope[”‘ + this.uniqueId + ‘”][”updateTime”]();’, 999 );
} else {
this.timer = setInterval ( function( that ) { that.updateTime(); }, 999, this );
}
}
TimeTicker.prototype.updateTime = function()
{
window.status = this.uniqueId + ” : ” + this.countdown++;
}




codeplay
8:41 am
December 26, 2008
#238922

It seems there is a uniform way to resolve this problem for various browsers (at least on FF, Safari, Chrome & IE), call in the form of:

window.setInterval(function() { this.funcToBeCalled.apply(this); }, interval);
window.setTimeout(function() { this.funcToBeCalled.apply(this); }, t);
or
window.setInterval(function() { obj.funcToBeCalled.apply(obj); }, interval);
window.setTimeout(function() { obj.funcToBeCalled.apply(obj); }, t);

let me know if i am wrong, thanks! : yalpedoc AT gmail DOT com




1:52 am
January 7, 2009
#240749

in prototype you can make it using bind
curTimeout = setTimeout( function(){ this.DoSomething( ); }.bind(this), this.nTimeout );

http://alternateidea.com/blog/articles/2007/7/18/javascript-scope-and-binding




7:02 am
May 13, 2010
#291478

You could build your own bind method.

Function.prototype.bind = function(obj) {
var _method = this;
return function() {
return _method.apply(obj, arguments);
};
}

setTimeout(this.method.bind(this),delay);






 

Leave a Reply