0

basically I'm trying to create an image "feed" like an Facebook-type site but where every post is an image (sort of like IG I suppose), where all the images load in with particular divs and other elements attached to them. I've got my JS to the point where it's populating all the HTML and CSS correctly and everything looks good.

The only thing is the dataset we're supposed to use has some particular pictures that don't load, and for these specific elements, I don't want the entire set of divs to be drawn up only to not display an actual picture - this looks bad. I could hardcode it to just ignore certain numbers, but in the interest of scalability I'd like to find a better solution.

I'm using a for-loop to create all the HTML elements, assign classes, ids, etc. However, when I try to make a boolean variable that I set using the complete attribute or the onerror - it doesn't seem to work properly. I use a conditional to check the boolean and continue if the image doesn't exist.

This is the relevant section of the code:

    function testImage(img) {
        img.addEventListener('load', function () { imageexist = true });
        img.addEventListener('error', function () { imageexist = false });
    }

 for (let i = 0; i < res.length; i++) {
        //check image load
        let img = document.createElement("img");
        img.src = res[i];
        testImage(img);
        // img.onerror = function () {
        //     imageexist = false;
        // }
        // 
        setTimeout(console.log(imageexist), 500);
        if (imageexist === false) {
            continue;
        }
        // console.log(img.complete);
        // if (!img.complete) {
        //     continue;
        // }
}

As you can see, I've tried setting a boolean variable (imageexist) via both a helper function and as the result of img.onerror, as well as testing img.complete directly. Unfortunately neither of these seem to work - they either skip every element if I default imageexist to false, or skip none if I default it to true. the res is just the array of image URLs.

edit: as per first comment, should have clarified probably that I only moved the definition of the boolean variable outside of the for loop in order to test the testImage function - moving it back inside the for loop and trying the img.onerror or img.complete methods still does not give the correct result.

JD3
  • 33
  • 1
  • 2
  • 6
  • Sorry - should have clarified probably that I only moved the definition of the boolean variable outside of the for loop in order to test the testImage function - moving it back inside the for loop and trying the img.onerror or img.complete methods still does not give the correct result. – JD3 Mar 06 '23 at 01:51
  • Just to clarify that question... Because it look like a [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem/66378#66378) to me. --- Let say you have an array of 50 image urls. The `for` loop wil go thought it all and create the 50 `img` elements before the very first image (or smallest one) even loads. -- **Loops do not "wait".** Then, what if an image does not load? The img element is shown with a missing image icon, and that is what you wish to remove, right? – Louys Patrice Bessette Mar 06 '23 at 02:03
  • @JD3: Do any of the answers answer your question? If not, please update your question with more details so that it can be re-opened. If yes, please consider accepting one of the answers (see gray checkmark) – Peter Thoeny Mar 10 '23 at 21:10

2 Answers2

0

Image load is asynchronous.

If you wish to show only loaded images, just add them an hidden class such as:

.hidden{
  display: none;
}

So yes, just add that class to every images:

for (let i = 0; i < res.length; i++) {
  // Create img element
  let img = document.createElement("img")
  img.src = res[i]
  img.classList.add("hidden")
  
  img.addEventListener("load", (event) => event.target.classList.remove("hidden"))

  // You certainly have some code to add the img to DOM.
  // ...
}

When they will be loaded, the event listener, attached to every of those, will remove the hidden class.


By the way, the continue keyword in a loop is to skip the rest of the code and go right away to the next iteration. MDN documentation

Louys Patrice Bessette
  • 33,375
  • 6
  • 36
  • 64
0

The problem with your code is that you loop through your response array, test and immediately reference the imagexist variable that is actually set later once the image is loaded.

You need to wait until the image is loaded:

    function addImage(url) {
        let img = document.createElement("img");
        img.addEventListener('load', function () {
            img.src = url;
            // and add to DOM
        });
        img.addEventListener('error', function () {
            console.log('Image load error: ' + url)
        });
    }

    for (let i = 0; i < res.length; i++) {
        addImage(res[i]);
    }

An alternative to this is to pass the full array to the addImage function, and to unshift an item, then call itself recursively on load' and on error`.

Peter Thoeny
  • 7,379
  • 1
  • 10
  • 20