I see that you are creating closures inside loops, which is a common mistake . To avoid this, you can create a more general closure outside the loop that processes all loading events and performs the function after loading all images:
// This array must be filled somewhere... var tileData = []; var ImageLoader = function(){ var numOfImages = 0; var numLoaded = 0; var callBack = function(){}; function imageLoaded(){ numLoaded++; if(numLoaded===numOfImages){ // All images are loaded, now call the callback function callBack.call(this); } } function init(numberOfImages, fn){ numOfImages = numberOfImages; callBack = fn; } return { imageLoaded: imageLoaded, init: init }; }(); function tryDisplayMosaic(){ alert('All images downloaded'); } function generate(){ // (1) Set the number of images that are to be loaded and (all tiles + penguin) // (2) Set the function that must be called after all images are loaded ImageLoader.init(tileData.length+1, tryDisplayMosaic); // Load this Penguins image var image = new Image(); image.src = 'Penguins.jpg'; image.onload = ImageLoader.imageLoaded; // Go through each image and load it for (var i=0; i<tileData.length; i++) { image = new Image(); image.src = tileData[i].ImageUrl; image.onload = ImageLoader.imageLoaded; } }
This code is completely different from your code, but in my opinion, it is much prettier. To do this, you need to set the number of downloaded images and the function that must be performed after all images have been downloaded.
Note. I have not tested it, I'm not sure about this part of .call(this) , maybe the forum can help if it is incorrect.
I tested my code and it works great. In addition, I improved it, so you keep a pointer to the loaded image inside the tileData array, see JSBin for an example.
source share