3

I think I'm missing an obvious rule here and it would be great to get it clarified -

I'm trying to write an image preloader and I'm following the advice given on this posting: Preloading images in Javascript? Without jQuery

I can see how it all works up until:

    for(var i = 0; i < imageList.length; i++ ) {
        var imageObject = new Image();
        imageObject.src = imageList[i]; }

I keep thinking this would simply change/overwrite the src property at every iteration, leaving imageObject.src as the last image in the list at the end of the loop, though clearly the function is meant for using multiple images.

So I'm assuming it leaves imageObject containing an array of images but I'm not sure how it does this. What do I have wrong?

Community
  • 1
  • 1
dnv
  • 969
  • 1
  • 7
  • 15
  • 2
    `imageObject` is left as a single image which isn't meant to be used in anything. That code simply preloads each image url in `imageList` – Joe Simmons Nov 17 '13 at 15:27
  • Could I ask how this works to preload multiple image if it is being overwritten? How would I draw all three images after using this code? I'm still trying to figure out how all this works... Thanks for pointing me to the loop closure thing, it was an accepted answer to someone elses question so I was trusting it. – dnv Nov 17 '13 at 15:35
  • 1
    That should work just fine for preloading, as the browser starts downloading the image once the source is set, but you are correct in that it only keeps the last image, the other images are overwritten, but still preloaded. – adeneo Nov 17 '13 at 15:35
  • 1
    Sorry about my first comment, I was looking at your code out of context *(didn't view your link)*. Once the `src` property of an Image object changes in JavaScript, it will preload *(aka cache)* the image contents in the browser. To draw them, you don't use `imageObject` at all, that's only for preloading *(in this code, specifically)*; you would just use the strings in the `imageList` array, like **`myElement.innerHMTL += '';`** – Joe Simmons Nov 17 '13 at 15:42

2 Answers2

1

What you have will "probably" work. But, it's not the ideal way to do things because it relies on some undocumented aspects of a browser and garbage collection. See here and here for safer ways to do this. I'll try to explain what your code is doing.

When you do this line:

   var imageObject = new Image();

it is creating a new DOM image object and placing a reference to that object in the variable imageObject.

When you do this line:

    imageObject.src = imageList[i];

it is assigning the .src property of that new image object. This will NOT be overwriting the .src property on the previous image object.

But, because the previous image objects are not stored anywhere and no other javascript has any references to them, they are available for garbage collection by the browser and it is free to get rid of them at any time. To use this for reliable caching, you are hoping that the browser does not cancel the networking operation in progress that is loading the images and you are hoping that they still get into the browser cache so they are preloaded.

It is much safer to store the imageObject in an array as the other solutions I've pointed you to will do. This keeps them from being garbage collected so there is no risk that their image loading will be cancelled.

For example, it would be safer to do this:

var imgs = [];
for (var i = 0; i < imageList.length; i++ ) {
    var imageObject = new Image();
    imageObject.src = imageList[i]; 
    imgs.push(imageObject);
}

The previous two references to other solutions to this do something similar, but package this in a function and one also has the ability to notify you when all the images have finished preloading.

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thanks, this was partly what was confusing me, it seemed counter intuitive to have the images floating in the cache without being referenced by anything explicitly, but I'm a beginner so maybe I'm not making much sense.. One final question - in your first link I see you are pushing each iteration of img to an array - am I right in assuming that this is just a signpost for what's actually being passed along? As a new coder it seems strange to have multiples of the same var in an array each having been assigned to a different thing... – dnv Nov 17 '13 at 16:04
  • 1
    @dnv - a reference to the image you just created goes in the array and then on the next pass through the `for` loop, the local variable is reused to contain a new image object. You could assign anything to the local variable and it wouldn't affect what you already put in the array as they are different storage locations. – jfriend00 Nov 17 '13 at 16:12
  • Thanks, this makes sense to me now. – dnv Nov 17 '13 at 16:15
1
<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <script type="text/javascript">
        var imageList = new Array("dummyImg1.jpg", "dummyImg2.jpg");
        var imagePlaceholder = new Array(imageList.length);
        function waitImagesLoaded() {
            var allImagesLoaded = true;
            for (var i = 0; i < imageList.length && allImagesLoaded; i++) {
                allImagesLoaded &= imagePlaceholder[i].complete;
            }
            if (allImagesLoaded) {
                for (var i = 0; i < imageList.length; i++) {
                    var imgElemIdToReplace = "img" + String(i + 1);
                    replaceElem(imagePlaceholder[i], document.getElementById(imgElemIdToReplace));
                }
            } else {
                window.setTimeout(waitImagesLoaded, 500);
            }
        }
        function replaceElem(substituteElem, elemToReplace) {
            var parentNode = elemToReplace.parentNode;
            parentNode.replaceChild(substituteElem, elemToReplace);
        }
    </script>
</head>
<body>
    <img id="img1" src="" alt="">
    <img id="img2" src="" alt="">
    <script type = "text/javascript">
       for (var i = 0; i < imageList.length; i++) {
            var imageObject = new Image();
            imageObject.src = imageList[i];
            imagePlaceholder[i] = imageObject;
        }
        window.setTimeout(waitImagesLoaded, 500);
    </script>
</body>
</html>
Michael Besteck
  • 2,415
  • 18
  • 10