Preloading Images into Leaflet Popup

Viewed 241

I'm currently adding some popups to my leaflet map of tourist destinations within a country. The popups will have a title, image and wiki link. I want to check that the images are available and preload them. I've succeeded in all of this, but now I don't know how to insert the preloaded images into the popup.

Here is my code:

var tourists = result.tourists.geonames;

        var images_urls = [];

        for (let i in tourists) {
            $.get(tourists[i].thumbnailImg)
                .done(function () {
                    images_urls.push(tourists[i].thumbnailImg);
                }).fail(function () {
                    images_urls.push("libs/css/image/wiki.png");
                })
            
        }

        var images = [];

        function preload(arguments) {
            for (var i = 0; i < arguments.length; i++) {
                images = new Image();
                images.src = arguments[i];
            }
        }

        preload(images_urls);
       

        for (let i in tourists) { 
            markers.addLayer(
                L.marker([tourists[i].lat, tourists[i].lng], { icon: touristIcon })
                    .bindPopup(`<b>${tourists[i].title}</b> <br> INSERT PRELOADED IMAGES <br> <a href="https://${tourists[i].wikipediaUrl}" target="_blank" >Wikipedia Article<a> <br>`)
            )
        }
2 Answers

Once you "preload" your image, it is stored in the browser cache, and you do not need to do anything special to benefit from that cache; the browser detects that you request the same path, and serves the file from its cache (provided that the file cache policy allows it).

So, ignoring for now possible unavailability of the file, you can just re-use the tourists[i].thumbnailImg URL.

But if I understand correctly, you would like to use the unavailability detection to display another image? The issue is that you push the result of this detection in a separate array images_urls, and that these results will likely be unordered (failing URL will probably take more time).

A simple solution could be to extend your tourists object/array instead:

for (let i in tourists) {
  $.get(tourists[i].thumbnailImg)
    .done(function () {
      // Add an extra property
      tourists[i].safeImgUrl = tourists[i].thumbnailImg;
    }).fail(function () {
      tourists[i].safeImgUrl = "libs/css/image/wiki.png";
    })
}
// preload
for (let i in tourists) {
  markers.addLayer(
    L.marker([tourists[i].lat, tourists[i].lng], {
      icon: touristIcon
    })
    .bindPopup(`<b>${tourists[i].title}</b> <br>
      ${tourists[i].safeImgUrl}<br>
      <a href="https://${tourists[i].wikipediaUrl}" target="_blank" >Wikipedia Article<a> <br>`)
  )
}

Note: you may want to wait some time before building the popup content, because your image availability detection may take some time before populating the new safeImgUrl properties. A possible way of achieving this could be to re-set the popup content on Marker click.

I finally figured out the problem! I needed to construct the popup before I bound it to the marker! So here is the code to check the URL works and then to preload the image.

function preloadImage(url, width, height) {
  var img = new Image(width, height);
  img.src = url;
  return img;
}

function checkImage(imageSrc, backup, width, height) {
  var http = new XMLHttpRequest();

  http.open('HEAD', imageSrc, true);
  http.send();

  if (http.status != 404) {
      return preloadImage(imageSrc, width, height)
  } else {
      return preloadImage(backup, width, height)
    }
}

Here is how to construct the popup:

 var tourists = result.tourists.geonames;

        for (let i in tourists) {

            var popup = document.createElement('div');
            var br = document.createElement('br');
            var picture_popup = checkImage(tourists[i].thumbnailImg, 'libs/css/image/wiki.png', 120, 100);
            var a = document.createElement('a');
            var linkText = document.createTextNode(tourists[i].title);
            a.appendChild(linkText);
            a.title = 'Wikipedia article';
            a.target = '_blank';
            a.href = 'https://' + tourists[i].wikipediaUrl;

            popup.appendChild(picture_popup);
            popup.appendChild(br);
            popup.appendChild(a);

            markers.addLayer(
                L.marker([tourists[i].lat, tourists[i].lng], {
                    icon: touristIcon
                })
                    .bindPopup(popup)
            )
        }

The only issue with it now is the relative backup URL not functioning, but it's basically there!

Related