midknyte
midknyte

Reputation: 617

Execute function after loading image array

I'm executing a script after the page header image loads:

objImg = new Image(); 
objImg.src = '/images/header.jpg';
objImg.onload = function() {
   $('#myDiv').fadeIn(500);
}

This works great. However on one of the pages I need it to execute after four images are loaded instead of just one. Is there a way to modify this to have it be an array instead of just one image?

Upvotes: 2

Views: 355

Answers (3)

kiranvj
kiranvj

Reputation: 34127

I would use something like this.

Use the same approach in your question..

For the array of images, give a class say image-wait-for-load to the img tag

Now find how many images are there in you page to be waited.

var waitImageCount = $('.image-wait-for-load').length;

Code to check if all images are loaded.

$(document /* or what ever */).on('load', '.image-wait-for-load', function() {
  waitImageCount--; // decrease the counter

  if(waitImageCount === 0) {
    // all images are loaded.
    // Do what ever you like
  }
}); 

Please note that this untested code, but I hope something like this should work.

Upvotes: 2

zer00ne
zer00ne

Reputation: 43950

The demo below uses an async function and await keyword so that a Promise controls the asynchronous interaction between client (you) and a live test server at the given endpoint (first parameter). A selector (second optional parameter) of a DOM element is passed to specify where to render images (default is "body" if not defined).

fetchImages("https://sub.domain.tld/path/to/json", "tagName#id.className[attribute]"/*or "body"*/)

fetch() is an asynchronous function that guarantees a response or rejection of requested data via the await keyword. The .json() method (also preceded by await) retrieves the JSON.

const response = await fetch(`https://api.myjson.com/bins/19fk22`);
let imgArray = await response.json();
/* At this point the JSON is stored in a variable called imgArray
imgArray = [
  "https://i.ibb.co/hZj77BZ/lena01.jpg",
  "https://i.ibb.co/7XxsBr5/lena02.png",
  "https://i.ibb.co/X7SCb3w/lena03.png"
]
*/

Next, the JSON array (aka imgArray) is ran through the array method .forEach((src, idx) => {.... Each url (src first parameter) within imgArray is processed. The following is a step-by-step breakdown of the first image url being processed (Note how the second parameter idx is used in step #3):

  1. Reference the DOM element that the images will be placed within

    const node = document.querySelector('.gallery')
    // <header class="gallery">|<= images will be inserted here =>|</header>
    
  2. Extract the file name of image with .split()

     let name = src.split('/')
     // ['https:', 'i.ibb.co', 'hZj77BZ', 'lena01.jpg']
     .pop()
     // 'lena01.jpg'
     .split('.')
     // ['lena01', 'jpg']
     .shift();
     // 'lena01'
    
  3. Assign a template literal of an htmlString to a variable (let html) and then interpolate ${values} and/or ${expressions}

     let html = `
    <figure style="animation: ${3 * (idx+1)}s fadeIn">`
    /* <figure style="animation: 3s fadeIn">
    Each iteration is assigned an increased CSS animation-duration value.
    The first image fades in for 3sec, the second image fades in for 6sec, third 9sec, etc.
    The actual code for the animation is in the CSS.
    */
      `<img src="${src}">`
      // <img src="https://i.ibb.co/hZj77BZ/lena01.jpg">
      `<figcaption>${name}</figcaption>`
      /* <figcaption>LENA01</figcaption>
      The name value from step #2 is inserted as a caption and styled by CSS
      */ 
    `</figure>`;
    node.insertAdjacentHTML("beforeend", html);
    /* .insertAdjacentHTML(position, htmlString) is .innerHTML on steroids
    @Param: [Position]: "beforebegin", "afterbegin", "beforeend", or "afterend"
    @Param: [htmlString]: strongly suggest that template literals be used instead of literal strings
    

Demo

const endpoint = `https://api.myjson.com/bins/19fk22`;

const fetchImages = async(endpoint, selector = "body") => {
  const response = await fetch(endpoint);
  let imgArray = await response.json();
  imgArray.forEach((src, idx) => {
    const node = document.querySelector(selector);
    let name = src.split('/').pop().split('.').shift();
    let html = `
    <figure style="animation: ${3 * (idx+1)}s fadeIn">
      <img src="${src}">
      <figcaption>${name}</figcaption>
    </figure>`;
    node.insertAdjacentHTML("beforeend", html);
  });
}

fetchImages(endpoint, '.gallery');
.gallery {
  display: flex;
  justify-content: center;
  width: 96%;
  margin: 10px auto;
  padding: 5px;
  background: rgb(138, 56, 201);
}

figure {
  width: 50vw;
  margin: 10px 2.5px 5px;
}

figcaption {
  font: 700 small-caps 3vw/1 Arial;
  color: gold;
  text-align: center;
}

img {
  width: 100%;
  height: auto;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  50% {
    opacity: 0.66;
  }
  to {
    opacity: 1;
  }
}
<header class='gallery'></header>

Upvotes: 0

Bilel
Bilel

Reputation: 1429

It could be done using Promises like here. Or simply loop while storing their sources in a parallel array and use a callback function when all images are preloaded. The parallel array, could be useful during loading errors too so we could use it to display only preloaded and valid images.

A more sophisticated approach would add a function to test image availability before even preloading it using XMLHttpRequest(); or a ajax.

Notice that I'm using a fake picture here to test loading failure.

var pix = new Array('https://image.shutterstock.com/image-photo/colorful-hot-air-balloons-flying-260nw-1033306540.jpg','https://images.fineartamerica.com/images-medium-large-5/hot-air-balloons-over-hay-bales-sunset-landscape-matthew-gibson.jpg','https://iso.500px.com/wp-content/uploads/2014/07/big-one.jpg','https://some.thing/undefined.jpg','https://cdn-media.rtl.fr/cache/4gx11M-ZCLtBdzqGMR2TWA/880v587-0/online/image/2019/1212/7799676803_l-enfant-de-the-mandalorian-est-la-star-de-la-galaxie.jpg');
 var total = pix.length;
 var ctr = 0;
 var cache=[];
 

for(var i=0; i<total; i++){
    var img = new Image();
    img.src = pix[i];    
    //We push it to the cached elements list
    cache.push(img);
    img.onload = function(){
        
        console.log('Pic loaded!');
        ctr++;
        //When all are loaded
        if(ctr == total){
            console.log('All done ;) ');
            //We append them to the targeted div 
            WellDone();
        }
    }
    img.onerror = function(){
        //Because we didn't test the picture loading before adding it, we have to remove the failing image from the array
        cache.splice(i, 1);
        ctr++;
        if(ctr == total){
            console.log('All done ;) ');
            //We append them to the targeted div 
            WellDone();
        }        
    }

}

function WellDone(){
    //finally here things could run in a sequence!
    for (var i = 0; i < cache.length; i++) {
    $('#myDiv').append(cache[i]);
    }
    
    cache=[];
$('#myDiv').fadeIn(500);
}
#myDiv{
display:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="myDiv"></div>

Upvotes: 1

Related Questions