webGuy
webGuy

Reputation: 163

jquery or javascript function after all images loaded

currently I am using the $( window ).load(function() to fire my action after all images loaded.

But sometimes when a external script or something else need a very long time to load, my functions fire very late even when the images are already loaded.

So I would like to find a function like $( document ).ready(function() which waits untill all images loaded and than run my function. I dont need the whole document to be ready, I just need the images to be loaded for my function.

Is there anything like this?

Upvotes: 0

Views: 1654

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1075765

Yes, it's actually fairly easy to do. Image elements have a complete flag on them, and they fire load or error events when they complete. So we can do this (see note below about the apparent complexity):

function getAllImagesDonePromise() {
    // A jQuery-style promise we'll resolve
    var d = $.Deferred();

    // Get all images to start with (even complete ones)
    var imgs = $("img");

    // Add a one-time event handler for the load and error events on them
    imgs.one("load.allimages error.allimages", function() {
        // This one completed, remove it
        imgs = imgs.not(this);
        if (imgs.length == 0) {
            // It was the last, resolve
            d.resolve();
        }
    });

    // Find the completed ones
    var complete = imgs.filter(function() { return this.complete; });

    // Remove our handler from completed ones, remove them from our list
    complete.off(".allimages");
    imgs = imgs.not(complete);
    complete = undefined; // Don't need it anymore

    // If none left, resolve; otherwise wait for events
    if (imgs.length == 0) {
        d.resolve();
    }

    // Return the promise for our deferred
    return d.promise();
}

And use it like this whenever you need to check:

getAllImagesDonePromise().then(function() {
    // They're all done
});

You could use it in a script you've put at the end of the body, just before the closing </body> tag (or in a ready callback if you prefer).

You're probably wondering about the complexity around "complete" images. Why do we hook an event on them, then remove them and unhook the event? It's to deal with race conditions. Although the browser runs the main JavaScript on a single UI thread, the browser is not single-threaded. It can finish loading an image and set its complete property at any time. If we're not already hooked to the load/error event on that image when that happens, we won't get notification. So if we filtered out completed images first, then hooked the events on the remaining ones, any image that completed between those lines of code (again: our code is single-threaded, the browser is not) would never fire the event. So we hook the events, then filter out completed ones, then deal with any events we get back.

Upvotes: 4

Related Questions