5

I am working on some code that will load a bunch (20+) of big images (~500 KB each) sequentially. After each image has loaded, it fades in. I used this fiddle from this discussion as a starting point.

I've got the images loading just the way I want, but I need to do a couple of other things without breaking this sequential loading. I need to load markup containing an iframe in between the third and fourth images, and I need to load a link after the images. Here is an example of the markup output I need:

<div id="container">
  <img src="img-1.jpg" />
  <img src="img-2.jpg" />
  <img src="img-3.jpg" />
  <div><iframe></iframe></div>
  <img src="img-4.jpg" />
  <img src="img-5.jpg" />
  <img src="img-6.jpg" />
  <img src="img-7.jpg" />
  <img src="img-8.jpg" />
  <img src="img-9.jpg" />
  <a href="/link/">Link text</a>
</div>

I can load images just fine, but I am stuck with how to load that iframe only after the first three have loaded, and then have the rest of the images load, and then the link. Here is my current javascript:

var no = 22,
main = [],
i;

for (i = 1; i <= no; i++) {
    main[i] = "path/to/image/folder/img-" + i + ".jpg";
}

function loadImages(arr,loc) {
if (arr.length === 0) {
    return;
}

function imageLoaded(img) {
$(img).hide().appendTo(loc).fadeIn(800);
}

function loadImage() {
    var url = arr.shift(),
    img = new Image(),
    timer;

    img.src = url;

    if (img.complete || img.readyState === 4) {
        imageLoaded(img);
        if (arr.length !== 0) {
            loadImage();
        }
    }
    else {
        timer = setTimeout(function() {
            if (arr.length !== 0) {
                loadImage();
            }
            $(img).unbind("error load onreadystate");
        }, 10000);
        $(img).bind("error load onreadystatechange", function(e) {
            clearTimeout(timer);
            if (e.type !== "error") {
                imageLoaded(img);
            }
            if (arr.length !== 0) {
                loadImage();
            }
        });
    }
}
    loadImage();
}

loadImages(main,"#container");

The "no" variable sets the number of images to cycle through (I need to do this on multiple pages with different numbers of images), and the loadImages function takes arguments for which array to cycle through, and where to put the images. This code could probably be a lot cleaner, but I'm new to javascript. Any help would be greatly appreciated!

TheVillageIdiot
  • 40,053
  • 20
  • 133
  • 188
Lee H
  • 79
  • 1
  • 6

3 Answers3

0

a possible solution would be to poll the length of the loaded images in each loadImage function call like so

loadImage(){
//stuff 
if($('#container img').length == 4){
    insertIframeMarkup(); 
}

//other stuff

}
Brad
  • 6,106
  • 4
  • 31
  • 43
  • This is working great for inserting that iframe exactly where I need it. I can't the link to load at the end though. Here's how I'm inserting the iframe: `if ($("#container img").length == 3) { $("#container img:last-child").after("
    ");` For the link at the end, I've tried putting this inside loadImages: `if (arr.length === 0) { linkMarkup(); return; }` and this inside loadImage: `if ($("#container img").length == 22) { linkMarkup(); }` The second will work for the second to last image, but not the last.
    – Lee H Feb 07 '13 at 17:48
  • I've got it now. This was the missing piece. Thanks everyone. – Lee H Feb 07 '13 at 22:16
0

Here is an example of how i did it

This is a little different than your approach but i think it give you the result what you're looking for.

var imgArr = ['http://healthystartups.com/storage/600px-MA_Route_1.png?__SQUARESPACE_CACHEVERSION=1319542839834', 'http://upload.wikimedia.org/wikipedia/commons/thumb/5/54/MA_Route_2.svg/600px-MA_Route_2.svg.png', 'http://foo.com/foo_873.jpg', 'http://3.bp.blogspot.com/-Q_owQUxNjdQ/Te3ALCmT3oI/AAAAAAAAAnk/wv9r0b0MT-g/s1600/600px-MA_Route_3.svg_.jpg', 'http://honestreviewscorner.com/wp-content/uploads/2012/10/the-number-4-in-a-circle.jpg1.png', 'http://www.thefrugalette.com/wp-content/uploads/2011/09/number-5.png'];

var count = 0;

function LoadImages(images) {
    img = images[count];
    img = new Image();
    img.src = images[count];

    //Between 3 and 4
    if(count == 4)
    {
        $('<iframe src="http://www.w3schools.com"></iframe>').appendTo('body');
    }


    if (img.complete || img.readyState === 4) {
        $(img).hide().appendTo('body').fadeIn(function () {
            count++;
            if (count < images.length) LoadImages(images);
            else $('body').append('<div>last line</div>')
        })

    } else {
        count++;
        LoadImages(images)
    }

}
LoadImages(imgArr);
Amin Eshaq
  • 4,034
  • 1
  • 17
  • 21
  • I like this for its simplicity, but I'm getting a call stack size exceeded when LoadImages(images) is called within the else statement. Any ideas? – Lee H Feb 07 '13 at 16:22
0

Here is working fiddle. The code follows:

var imgArr = ['http://healthystartups.com/storage/600px-MA_Route_1.png?__SQUARESPACE_CACHEVERSION=1319542839834', 
    'http://upload.wikimedia.org/wikipedia/commons/thumb/5/54/MA_Route_2.svg/600px-MA_Route_2.svg.png',
    'http://1.bp.blogspot.com/-jiW5NeKtZBY/T-nyLRuSSZI/AAAAAAAACaA/Ro8wjmk32ow/s1600/red-number-8.jpg',
    'http://3.bp.blogspot.com/-Q_owQUxNjdQ/Te3ALCmT3oI/AAAAAAAAAnk/wv9r0b0MT-g/s1600/600px-MA_Route_3.svg_.jpg',
    'http://us.123rf.com/400wm/400/400/zoomzoom/zoomzoom1102/zoomzoom110200070/8913747-4--created-by-light-colorful-digits-over-black-background.jpg',
    'https://twimg0-a.akamaihd.net/profile_images/1633986906/6digits-fist-Logo.png'],
    images=[imgArr.length],
    otherUrls = {}, urlTypes = {
        IFRAME: 0,
        LINK: 1
    };

//INITIALIZE OTHER URLS WITH URL AND INDEX AT WHICH IT WOULD BE LOADED
    function createInfo(url, type, text) {
        var o = {};
        o.URL = url;
        o.Type = type;
        o.Text = text;
        return o;
    }
otherUrls[2] = createInfo("http://http://en.wikipedia.org/wiki/Punjabi_language", urlTypes.IFRAME);
otherUrls[4] = createInfo("http://http://en.wikipedia.org/wiki/Numerical_digit", urlTypes.LINK, "click here [wikipedia]");

function loadImages(arr,loc,other){
    var l=arr.length,i=0, url, idx, o;
    while(arr.length){
        url=arr.shift(),
        idx=(l-arr.length)-1,
        o=other[idx];

        makeImage(url,idx,loc,o);
    }
}

function makeImage(url, idx, loc, other){
    var image=new Image();
    image.src = url;
    images[idx]=image;
    image.onload=function(){
            imageLoaded(idx, loc, other);
        };
    image.onerror=function(){
            var ix=idx,ot=other,lc=loc;
            imageError(ix, lc, ot);
        };
}

function imageLoaded(i,l,o){
    var img=$(images[i]);
    img.hide().css({'display':'block','height':'25px','width':'25px'}).appendTo(l).fadeIn();
    loadOtherObjects(o,img);
}

function imageError(i,l,o){
    // handle 404 using setTimeout set at 10 seconds, adjust as needed
    timer = setTimeout(function () {$(images[i]).unbind("error load onreadystate");}, 10000);
    $(images[i]).bind("error load onreadystatechange", function (e) {
        if (e.type !== "error") {
            imageLoaded(i,l,o);
        }
    });
}

function loadOtherObjects(o,img){
    if (o) {
        console.log(o);
        console.log(o.Type==urlTypes.LINK);
        if (o.Type == urlTypes.IFRAME) {
            var d = $("<div/>").css({'height':'250px','width':'250px'}), f = $("<iframe/>").attr("src", o.URL);
            f.appendTo(d);
            d.insertAfter(img);
        } else if(o.Type == urlTypes.LINK) {
            var lnk = $("<a/>").attr("href", o.URL).text(o.Text);
            lnk.insertAfter(img);
        }
    }
}

$(function(){
    loadImages(imgArr, "#container", otherUrls);
});

NOTE: Please see this answer to another question regarding loading images via code, as not all browsers fire loaded event if images is found in cache.

Community
  • 1
  • 1
TheVillageIdiot
  • 40,053
  • 20
  • 133
  • 188
  • This is nearly working, but its loading the images in a seemingly random order (5,3,4,6,1,2,etc., different each time I load). I'm using the code in my original question to dynamically create my array so that I don't have to have a ton of duplicate code listing the full file path to each image (they're all in the same place and there are 22 total). Any idea what's causing that behavior? – Lee H Feb 07 '13 at 16:25
  • okay that is being caused by the size of images. as we are using callbacks to handle load events so as soon as one is loaded, it is shown. What you can do is load first image and then trigger loading of next one from the `onload` handler function `imageLoaded` – TheVillageIdiot Feb 07 '13 at 23:11