Saturday, June 16, 2007

Simple setTimeout / setInterval extra arguments IE fix

One of the coolest thing of Internet Explorer is that even version 7 doesn't respect timeouts or intervals creation functions as every other browser does!

I'm talking about extra parameters, that we need to pass inside a "temporary" (not for GC) extra function to use closures and to have same result.

Here is an example:

// i need an interval that check a variable ...
// ... and this is the way we usually do that ...
var i = setInterval((function(start){
return function(){
if(new Date - start > 990) {
alert("about 1 second: ".concat(new Date - start));
clearInterval(i);
}
}
})(new Date), 20);


Well done, two functions for our browser and boring unelegant way to code.

With FireFox, Opera, Safari and other more standard browsers, we should send directly extra arguments after delay interval.

setInterval(callback, delay, param1, param2, ..., paramN);

This means at least two things:
1 - we don't need to create a function wrapper to send arguments to callback
2 - we don't need to write many bytes to send parameter that need to be wrapper arguments that need to be sent to callback

A lot of developer uses string mode to solve this problem:
setTimeout("callback()", 20);

but as You know, eval is evil and we always prefere don't use them every time we can.

In this case, we should use "stupid" string evaluation to solve an Internet Explorer unfeaure ... but do we really need to code in this way?

Nope, and this is my simple proposal:

/*@cc_on
(function(f){
window.setTimeout =f(window.setTimeout);
window.setInterval =f(window.setInterval);
})(function(f){return function(c,t){var a=[].slice.call(arguments,2);return f(function(){c.apply(this,a)},t)}});
@*/

Of course, a conditional comment to call that anonymous function only with IE browser, every other will use native setTimeout or setInterval function without problems.

Please note that usage of window.setSomething, instead of direct setSomething assignment is a must, because if You remove window from left side IE will tell You that operation is not valid while if You remove window from righ side, IE simply will never do anything with those two functions.

About IE Performances?
A bit slower intervals, it's obvious, but why should we care about a browser that allow us to changes its native functionality with just a simple trick like that?

I don't know if someone has just talked about this problem and this solution but I hope this one will be useful for your next code.

Oooops, I forgot first example? Here it is ;-)
var i = setInterval(function(start){
if(new Date - start > 990) {
alert("about 1 second: ".concat(new Date - start));
clearInterval(i);
}
}, 20, new Date);

9 Comments:

Blogger kentaromiura said...

you can't be andrea! who are you??
Andrea will never use conditional comment.......



:pheasant:

16 June, 2007 13:42  
Blogger Andrea Giammarchi said...

this time conditiona comments are a must to don't change every other browser behaviour but if You prefere, You could use old good if(window.ActiveXObject)

:D

16 June, 2007 13:56  
Blogger Renato said...

Hey there, Andrea!
Sounds like a good idea, but I tried it and simply can't make it work.

Not even your sample code.
Any clues ?

12 July, 2007 19:59  
Anonymous Anubis said...

Hello Andrea,

I have implemented your coding and it works fantastically! One thing though, I have used the string version of window.setTimeout in many other places, is there a way to alter your cc function to detect for two arguments and if there is only two specified, use the standard setTimeout behavior and eval the string? Would just add a little backward compatibility...

Thanks again for such a useful piece of info!

04 October, 2007 05:15  
Blogger Andrea Giammarchi said...

Hi Anubis, probably this version should be useful for your goal ?

/*@cc_on
(function(f){
window.setTimeout = f(window.setTimeout);
window.setInterval = f(window.setInterval);
})(function(f){
return function(c,t){
var a = Array.prototype.slice.call(arguments,2);
if(typeof c != "function")
c = new Function(c);
return f(function(){
c.apply(this, a)
}, t)
}
});
@*/

04 October, 2007 06:56  
Anonymous Anonymous said...

Hi Andrea,

Again, much appreciation for your efforts, that was perfect!

Thanks.
Anubis.

06 October, 2007 02:19  
Anonymous Anonymous said...

Thanks for the help. I've been searching for a couple of weeks and found your solution. Does exactly what I needed. You rock!

24 October, 2007 01:12  
Anonymous Guille said...

Thank you very much for this, it worked perfectly for me.

24 October, 2007 17:21  
Anonymous Anonymous said...

Hey, you rox!! thanks for that funcion, it work perfect for me :)

04 March, 2009 10:29  

Post a Comment

Links to this post:

Create a Link

<< Home