1

I am trying to pre-load many (thousands) of images, and thought I was doing it right. I have all the URLs (some are valid, some are not) in an array. I loop through the array, and attache onload and onerror events to the img.src function. When the image event returns an error I do not add it to the "good" array, and I continue in my loop.

However, I have noticed that, while this should prevent images from making it into my "good" array, it does not always (Actually I have enough images that I can't tell if it ever does). When I actually go to load those images into the page, I get the broken image symbol and a 404 in my console.

I see 404 errors in my console while pre-loading, so I am assuming it does detect some broken images but not all, or maybe they still make it into my other array? Could it be that the images are being loaded so fast that the continue statement I have does not work (There are thousands)? If so, is there a way around this? I have attached my code below, here I tried using continue in the .onerror condition but I guess the img.src made it an invalid loop condition. Thank you for any help.

EDIT:

The src attribute is one property of an object, it also will have name and userName properties, so I only want to add the objects with valid urls. I tried to abbreviate my code but should have added this part (I only added the first three lines, even though I now realize I should push the item onload

var name = 'test',
    username = 'testUser'
    url;
for(i = 0; i < imgURLs.length; i++) {
    url = url[i];
    var img = new Image();
    valid = true;
    img.onload = function(){
        console.log('New Media Loaded')
    };
    img.onerror = function(){
        console.log('error: bad image source');
        valid = false;
    };
    img.src = url;
    if(valid) {
       goodArray.push(img);
    }
}
Startec
  • 12,496
  • 23
  • 93
  • 160

2 Answers2

3

onerror is asynchronous. It happens sometime in the future. You are testing the valid variable long before onerror or onload has even run. If you only want to push good images into the array, then you should just push them into the array in the onload handler like this:

for(i = 0; i < imgURLs.length; i++) {
    var img = new Image();
    img.onload = function(){
        console.log('New Media Loaded')
        goodArray.push(this);
    };
    img.onerror = function(){
        console.log('error: bad image source');
    };
    img.src = url;
}

FYI. Note, I'm also pushing this, not img because the variable img has been changed by the subsequent iterations of your for loop, but this will be the image that just successfully loaded.


If you want an image preloader that will notify when the last image has loaded, you can use the code form this answer.

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thank you. I did not specify in my code that this was one property of a larger object that needs to be pushed (I just edited it), if you were to initialize / add the entire object, would you place it where I did (after you set the `url` in the `onload` part? – Startec Jun 10 '14 at 02:49
  • @Startec - The ONLY place you know whether the image loaded successfully is inside the `onload` handler. So, if you only want to push good images, then you have to either push them from there or call a function from there that will push them. Those are your only two choices. – jfriend00 Jun 10 '14 at 02:52
  • About your FYI... Would `this` or declaring a sort `o = new Object(); o.pic = img` always refer to the "correct" image, (i.e. the URL that was in the loop at the time of creation). In other words would they "remember" their img attribute even as the loop continues on to other images. (similarly would `var` not?) – Startec Jun 10 '14 at 04:20
  • @Startec - I'd have to see the actual code you're asking about. Local variables and properties of local variables overwritten in the `for` loop will not work for this. You could create a closure, if you have other things to store - examples on doing that here: http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – jfriend00 Jun 10 '14 at 04:23
  • @Startec - I can't follow exactly what code you're asking about. – jfriend00 Jun 10 '14 at 04:47
1

Javascript is asynchronous. Your condition ( if(valid) ) is tested before loading. If you want to push the good images in an array, push it in the "onload" event.

for(i = 0; i < imgURLs.length; i++) {
    var img = new Image();

    img.onload = function(){
        goodArray.push(this);
        console.log('New Media Loaded')
    };

    img.onerror = function(){
        console.log('error: bad image source');
    };

    img.src = url;
}

After, if you want to add an action after loading all images, add a counter like that

var imagesCount = imgURLs.length,
    counter     = 0;

for(i = 0; i < imagesCount; i++) {
    var img     = new Image();

    img.onload = function(){
        goodArray.push(this);
        console.log('New Media Loaded')

        counter++;

        if(counter == imagesCount) yourAction();
    };

    img.onerror = function(){

        console.log('error: bad image source');

        counter++;

        if(counter == imagesCount) yourAction();
    };

    img.src = url;
}

function yourAction(){
    // your final action here ...
}
Anthony
  • 121
  • 7