Reputation: 163
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
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