My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

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);

12 comments:

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



    :pheasant:

    ReplyDelete
  2. 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

    ReplyDelete
  3. 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 ?

    ReplyDelete
  4. 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!

    ReplyDelete
  5. 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)
    }
    });
    @*/

    ReplyDelete
  6. Hi Andrea,

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

    Thanks.
    Anubis.

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

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

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

    ReplyDelete
  10. Please Andrea, can you update your post code with the comment-backward-compat-version?

    thanks! :-)

    ReplyDelete
  11. Works like a charm! Exactly what I was looking for! Thanks a lot!

    ReplyDelete

Note: Only a member of this blog may post a comment.