How to verify when multiple images have been loaded in JavaScript

Some applications that work a lot with images, will introduce the developer a well known problem, how to know simultaneously when at least 2 images are loaded in the browser. Every image in the DOM can be manipulated with JavaScript and it's pretty easy to know when an image has been succesfully loader or not. However there is no such a default method to know when a group of images have been loaded. As the way in you load your images may not be in the background but directly in the view or you are working directly with DOM objects, there is no universal function that can do it automatically for you, however you can learn a very simple trick used by many developers to know wheter 2 images have been succesfully loaded or not.

How does the correct approach works?

Theoretically, to verify when all images load, you will need to have a flag variable that works as a counter. This counter will increment everytime an img is succesfully loaded or not and someway another function needs to be triggered everytime this happens, so your callback will be executed yes or yes when the process finishes. This function verifies if the counter value is equal to the number of images that you want to load and when this happens, the callback is executed. Your callback will receive as first argument the images that were not loaded:

// Create the flag variables (counter and total of images)
var Counter = 0;
var TotalImages = 2;

// Create an instance of your images
var Image1 = new Image();
var Image2 = new Image();

// Store the images that were not correctly loaded inside an array to show later
var notLoadedImages = [];

// The onload callback is triggered everytime an image is loaded
var onloadCallback = function(){
    // Increment the counter
    Counter++;

    // Verify if the counter is less than the number of images
    if(Counter < TotalImages){
        return;
    }

    // Trigger the final callback if is the last img
    allLoadedCallback();
};

// The onerror callback is triggered everytime an image couldn't be loaded
var onerrorCallback = function(){
    // Increment the counter
    Counter++;
    
    // Add the current image to the list of not loaded images
    notLoadedImages.push(this);

    // Verify if the counter is less than the number of images
    if(Counter < TotalImages){
        return;
    }

    // Trigger the final callback if is the last img
    allLoadedCallback();
};

// The callback that is executed when all the images have been loaded or not
var allLoadedCallback = function(){
    console.log("Atention, the following images were not loaded ", notLoadedImages);
};

// Attach onload callbacks
Image1.onload = onloadCallback;
Image2.onload = onloadCallback;

// Attach onerror callbacks
Image1.onerror = onerrorCallback;
Image2.onerror = onerrorCallback;

// Load the images !
Image1.src = "queso.png";
Image2.src = "image.jpg";

Note that the allLoadedCallback is executed only once. Obviously the code is just an example of how the logic works, so you can wrap it and make it work with more than 2 images. The following step shows a simple function that allows you to load multiple images with a single function an execute a single callback that notifies which images were loaded or not.

Success and error implementation

The following ImageLoader method works in the following way: you need to provide as first argument an array with the URLs of the images that you want to load. Internally the function will load it using the Image class and loop through every given URL of the image. The internal counter increments as mentioned in the basic logic when an image loads or not and is stored inside an object that will be returned later in the onAllLoaded callback:

Note

As the browser is the one that loads the image, you don't have to worry about the performance of the download as the image will be surely cached and can be used subsequently to render them in <img> tags.

/**
 * Loader function that helps to trigger a callback when multiple images has been loaded. Besides
 * indicates which images were correctly/wrong loaded.
 * 
 * @param {Array} Images An array of strings with the paths to the images.
 * @param {Function} Callback Callback function executed when all images has been loaded or not.
 */
function ImageLoader(Images, Callback){
    // Keep the count of the verified images
    var allLoaded = 0;

    // The object that will be returned in the callback
    var _log = {
        success: [],
        error: []
    };

    // Executed everytime an img is successfully or wrong loaded
    var verifier = function(){
        allLoaded++;

        // triggers the end callback when all images has been tested
        if(allLoaded == Images.length){
            Callback.call(undefined, _log);
        }
    };

    // Loop through all the images URLs
    for (var index = 0; index < Images.length; index++) {
        // Prevent that index has the same value by wrapping it inside an anonymous fn
        (function(i){
            // Image path providen in the array e.g image.png
            var imgSource = Images[i];
            var img = new Image();
            
            img.addEventListener("load", function(){
                _log.success.push(imgSource);
                verifier();
            }, false); 
            
            img.addEventListener("error", function(){
                _log.error.push(imgSource);
                verifier();
            }, false); 
           
            img.src = imgSource;
        })(index);
    }
}

Then it can be used like this:

ImageLoader(["example.png", "example.jpg", "http://i.imgur.com/fHyEMsl.jpg"], function(result){
    if(result.error){
        // outputs: ["example.png", "example.jpg"]
        console.log("The following images couldn't be properly loaded: ", result.error);
    }

    // outputs: ["http://i.imgur.com/fHyEMsl.jpg"]
    console.log("The following images were succesfully loaded: ", result.success);
});

The advantage of this approach is that you will know which images were loaded or not. Note that with this function you will be able to verify strings to the path to the images as Image instances won't work (like an already selected img DOM element as the onload callback won't be triggered).

Happy coding !

This could interest you

Become a more social person