treehouse : what would you like to learn today?
Web Design Web Development iOS Development

[Solved] Can I sync up multiple image onload calls?

  • 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?
  • 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[i].tagName === "IMG") {
    c = (c && this[i].complete && this[i].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.
  • @mottie

    Thanks, but not quite what I needed. I should have been more specific. These images aren't been loaded into image 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.
  • 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?
  • Yes, it has to load the image first to use its data to draw on the canvas.
  • 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[i];
    }

    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