1

I am using a for loop on a data array, for each element in an array, the element's id is equal to the name of an img. For example: if the id of an element is 1, there should be a corresponding image in the image folder named as 1.

Problem: Both jpg and png format exist in the img folder, so I have to check if an image with a specific format exit or not, if it doesn't, then I have to change .jpg to .png.

I am going to use it with jQuery .on('error') function, but the problem is .on('error') runs after the entire for loop, so every time when I try to get the id, I get the last element of an array.

Code :

for (var i = 0; i < organisers_data.length; i++) {
  console.log("current ID" + organisers_data[i].id);
  organ_data_id = organisers_data[i].id.replaceAll(/^0+(?!$)/g, "");

  var imgPath = '<img src="/assets/images/organisers/' + organ_data_id + '.jpg" />';

  console.log("before" + imgPath)
  $(imgPath).on('error', function(e) {
    imgPath = '<img src="/assets/images/organisers/' + organ_data_id + '.png" />';
  });

  var tableDiv = '';
  tableDiv +=
    '<div>' +
    '<a href="">' +
    imgPath +
    '</a>' +
    '</div>';

  if ($('.organisers_table')) {
    $('.organisers_table').append(tableDiv)
  }
}
}

console shows that

$(imgPath).on('error', function(e) {
    imgPath = '<img src="/assets/images/organisers/' + organ_data_id + '.png" />';
});

only take the last element id ,seems the on function work only after the for loop, but what I want is id of an element in each loop, what should I do?

Chris Barr
  • 29,851
  • 23
  • 95
  • 135
  • "*seems the on function work only after the for loop*" - yes, that's how events work. Javascript is single-threaded, so only one bit of code can be running at a time - your `for` loop. When that finishes, any events can fire. Your issue is that `organ_data_id` is a global variable - define it within the `for` using `let organ_data_id = organisers_data[i].id...` – freedomn-m Aug 07 '23 at 15:50

2 Answers2

1

The problem you are facing is that onerror is asynchronous. By the time it fires it the code is already done running after it. So you need to change how your code works to handle the asynchronous nature.

One way is to let DOM fire the event and you replace the image that is added to the page. You can create the element and use the event listener to change the source of the image.

const tbl = document.querySelector('.organisers_table');
for (var i = 0; i < organisers_data.length; i++) {

  const organ_data_id = organisers_data[i].id.replaceAll(/^0+(?!$)/g, "");

  const wrapper = document.createElement("div");
  
  const link = document.createElement("a");
  link.href = "#";
  
  const img = document.createElement("img");
  img.src = '/assets/images/organisers/' + organ_data_id + '.jpg';
  img.addEventListener('error', function () {
    this.src = '/assets/images/organisers/' + organ_data_id + '.png';
  });
  
  
  link.appendChild(img);
  wrapper.appendChild(link);
  tbl.appendChild(wrapper);
  
}

Another way is using fetch, promise.all, and map to load the data.

const loadImageRecord = async(record) => {
  const id = record.id.replaceAll(/^0+(?!$)/g, "");

  const path = `/assets/images/organisers/${id}.`;

  const jpg = `${path}jpg`;
  const png = `${path}png`;

  return fetch(jpg, {
      method: 'HEAD',
    })
    .then(() => jpg)
    .catch(() => png)
    .then(src => `
    <div>
      <a href="">
        <img src="${src}" />
      </a>
    </div>`);
};


const loadImageTable = async() => {
  const requests = organisers_data.map(loadImageRecord);
  const records = await Promise.all(requests);

  const tbl = document.querySelector('.organisers_table');
  tbl.innerHTML = records.join('');
}
epascarello
  • 204,599
  • 20
  • 195
  • 236
0

i think you need something like :

async function imageExists(imgUrl) {
if (!imgUrl) {
    return false;
}
return new Promise(res => {
    const image = new Image();
    image.onload = () => res(true);
    image.onerror = () => res(false);
    image.src = imgUrl;
});
}

and use it inside your loop to find it is jpg or png like :

await imageExists(yourUrl)

and if it results was true use it in the tag if not use another one