I spent 3 hours banging my head against a chunk of JavaScript that just wouldn’t work. Finally got it. Putting it in the blog for the sake of posterity.
So I’m trying to write an image preloader, and I have something like this:
// this is bad
function imageLoaded(i) {
alert(i);
}
for (var i = 0; i < max; i++) {
var image = new Image();
image.onload = imageLoaded(i);
// more irrelevant stuff here
}
My intent was to have imageLoaded(i)
be called when the image was done loading. Innocent enough, but completely wrong. As is, imageLoaded(i)
will be called immediately and its return value will be set to image.onload
(which in my case, doesn’t even make sense). Once I finally realized this, I tried something different:
// better, but still not good
function imageLoaded(i) {
alert(i);
}
for (var i = 0; i < max; i++) {
var image = new Image();
image.onload = function () { imageLoaded(i) };
// more irrelevant stuff here
}
What image.onload
wants is an actual function object. So I give it an anonymous function, with my imageLoaded(i)
call tucked safely inside, not to be executed until the anonymous function is called. This worked great.
Well, except one thing. The alerts kept showing me the same max value for i
, instead of the differing values I expected. It was using the value of i
at the time the anonymous function was called (which happens after the for loop is done) instead of the value at the time I declared the anonymous function.
Fortunately, at about this point, I found an article that covers this issue as well as validating the tinkering I’d been doing so far. With a clever scoping trick, I was able to get the correct value of i
to each imageLoaded(i)
call. The article explains it better than I can, so check it out here: Passing Variables to Referenced Functions.
My final solution:
// hooray!
function imageLoaded(i) {
return function () {
alert(i);
}
}
for (var i = 0; i < max; i++) {
var image = new Image();
image.onload = imageLoaded(i);
// more irrelevant stuff here
}