Grow your CSS skills. Land your dream job.

Can I sync up multiple image onload calls?

  • # December 29, 2011 at 2:22 pm

    I want a function to run when specific images are loaded, but I don’t know how to wait for both to load before running. I only know how to chain them, like below:

    Image1 = new Image();
    Image1.src = 'image1-link.jpg';

    Image2 = new Image();
    Image2.src = 'image2-link.jpg';

    Image1.onload = function() {
    Image2.onload = function() { ... }
    }

    The downside to this is it has to wait till Image1 completely loads before getting the second. I want to try something like this:

    Image1 = new Image();
    Image1.src = 'image1-link.jpg';

    Image2 = new Image();
    Image2.src = 'image2-link.jpg';

    (Image1 && Image2).onload = function() { ... }

    How can I do this?

    # December 29, 2011 at 7:08 pm

    Hi Noah!

    Actually recently blogged a snippet of code/mini plugin that basically does this.

    Here is the code:

    /*
    Check if all images are loaded
    - Callback occurs when all images are loaded
    - image load errors are ignored (complete will be true)
    - Use:
    $('.wrap img').imagesLoaded(function(){
    alert('all images loaded');
    });
    */

    jQuery.fn.extend({
    imagesLoaded: function( callback ) {
    var i, c = true, t = this, l = t.length;
    for ( i = 0; i < l; i++ ) {
    if (this.tagName === "IMG") {
    c = (c && this
    .complete && this.height !== 0);
    }
    }
    if (c) {
    if (typeof callback === "function") { callback(); }
    } else {
    setTimeout(function(){
    jQuery(t).imagesLoaded( callback );
    }, 200);
    }
    }
    });

    If you use $(window).load(){ /* code here */ }); then you already know all images are loaded. But if you lazy load or add more images after the page has loaded, this is an alternative method.

    Here is how to use it to run the callback after multiple images have finished loading:

    $(function(){
    $('.wrap img').imagesLoaded(function(){
    alert('all images loaded');
    });
    });

    Or, if you only have one image, just target that image

    $('img#fred').imagesLoaded(function(){
    alert('Fred!');
    });

    If there are no images in your selector, it automatically runs the callback by default.

    # December 29, 2011 at 7:30 pm

    @mottie

    Thanks, but not quite what I needed. I should have been more specific. These images aren’t been loaded into tags, I’m using them in an HTML5 canvas. The user is able to change what’s drawn on the canvas via buttons and I want to make sure the images related to that are all loaded up before running all my other code.

    # December 29, 2011 at 7:52 pm

    It should be the same basic idea… I’m still learning canvas, but it still needs to load images before it can render them, right?

    # December 29, 2011 at 8:02 pm

    Yes, it has to load the image first to use its data to draw on the canvas.

    # December 30, 2011 at 1:41 pm

    Here are 2 answers via stack overflow:

    // loader will 'load' items by calling thingToDo for each item,
    // before calling allDone when all the things to do have been done.
    function loader(items, thingToDo, allDone) {
    if (!items) {
    // nothing to do.
    return;
    }

    if ("undefined" === items.length) {
    // convert single item to array.
    items = [items];
    }

    var count = items.length;

    // this callback counts down the things to do.
    var thingToDoCompleted = function (items, i) {
    count--;
    if (0 == count) {
    allDone(items);
    }
    };

    for (var i = 0; i < items.length; i++) {
    // 'do' each thing, and await callback.
    thingToDo(items, i, thingToDoCompleted);
    }
    }

    function loadImage(items, i, onComplete) {
    var onLoad = function (e) {
    e.target.removeEventListener("load", onLoad);

    // this next line can be removed.
    // only here to prove the image was loaded.
    document.body.appendChild(e.target);

    // notify that we're done.
    onComplete(items, i);
    }
    var img = new Image();
    img.addEventListener("load", onLoad, false);
    img.src = items;
    }

    var items = ['http://bits.wikimedia.org/images/wikimedia-button.png',
    'http://bits.wikimedia.org/skins-1.18/common/images/poweredby_mediawiki_88x31.png',
    'http://upload.wikimedia.org/wikipedia/en/thumb/4/4a/Commons-logo.svg/30px-Commons-logo.svg.png',
    'http://upload.wikimedia.org/wikipedia/commons/3/38/Icons_example.png'];

    loader(items, loadImage, function () {
    alert("done");
    });

    and here is a jsFiddle link for you http://jsfiddle.net/8baGb/1/
    Second solution:

    var imageCollector = function(expectedCount, completeFn){
    var receivedCount;
    return function(){
    if(++receivedCount == expectedcount){
    completeFn();
    }
    };
    }();

    var ic = imageCollector(2, function(){alert("Done!");});
    Image1.onload = ic;
    Image2.onload = ic;

    And here is a link to the Stack Overflow thread

Viewing 6 posts - 1 through 6 (of 6 total)

You must be logged in to reply to this topic.

*May or may not contain any actual "CSS" or "Tricks".