Lazy Loading HTML5 picture tag

2019-04-06 23:01发布

I have been searching (unsuccessfully) for a reliable method to lazy load images while using the HTML5 spec for <picture>. Most solutions/plugins out there currently rely on using data- attributes. I could be wrong, but it doesn't seem this method will work in conjunction w/ <picture>.

I'm really just looking to be pointed in the right direction. If anyone has a solution that they're currently using, I'd love to see. Thanks!

UPDATE:
Here is standard markup per the HTML5 spec:

<picture width="500" height="500">
    <source media="(min-width: 45em)" src="large.jpg">
    <source media="(min-width: 18em)" src="med.jpg">
    <source src="small.jpg">
    <img src="small.jpg" alt="">
</picture>

2条回答
The star\"
2楼-- · 2019-04-06 23:45

For anyone still interested... After revisiting this issue, I came across a fairly new script called, Lazysizes. It's actually quite versatile, but more importantly it allows me to do lazy loading of images while utilizing the HTML5 markup as described in the OP.

Much thanks to the creator of this script, @aFarkas.

查看更多
Luminary・发光体
3楼-- · 2019-04-06 23:49

Working example of lazy loading images using the picture element and intersection observer tested in Chrome and Firefox. Safari doesn't support intersection observer so the images are loaded immediately, and IE11 doesn't support the element so we fallback to the default img

The media queries in the media attr are arbitrary and can be set to suit.

The width threshold set is 960px - try a reload above and below this width to see either the medium(-m) or large(-l) variation of the image being downloaded when the image is scrolled into the viewport.

Codepen

<!-- Load images above the fold normally -->
<picture>
  <source srcset="img/city-m.jpg" media="(max-width: 960px)">
  <source srcset="img/city-l.jpg" media="(min-width: 961px)">
  <img class="fade-in" src="img/city-l.jpg" alt="city"/>
</picture>

<picture>
  <source srcset="img/forest-m.jpg" media="(max-width: 960px)">
  <source srcset="img/forest-l.jpg" media="(min-width: 961px)">
  <img class="fade-in" src="img/forest-l.jpg" alt="forest"/>
</picture>

<!-- Lazy load images below the fold -->
<picture class="lazy">
  <source data-srcset="img/river-m.jpg" media="(max-width: 960px)">
  <source data-srcset="img/river-l.jpg" media="(min-width: 961px)">
  <img data-srcset="img/river-l.jpg" alt="river"/>
</picture>

<picture class="lazy">
  <source data-srcset="img/desert-m.jpg" media="(max-width: 960px)">
  <source data-srcset="img/desert-l.jpg" media="(min-width: 961px)">
  <img data-srcset="img/desert-l.jpg" alt="desert"/>
</picture>

and the JS:

    document.addEventListener("DOMContentLoaded", function(event) {
   var lazyImages =[].slice.call(
    document.querySelectorAll(".lazy > source")
   )

   if ("IntersectionObserver" in window) {
      let lazyImageObserver = 
       new IntersectionObserver(function(entries, observer) {
          entries.forEach(function(entry) {
           if (entry.isIntersecting) {      
              let lazyImage = entry.target;
              lazyImage.srcset = lazyImage.dataset.srcset;
              lazyImage.nextElementSibling.srcset = lazyImage.dataset.srcset;
              lazyImage.nextElementSibling.classList.add('fade-in');
              lazyImage.parentElement.classList.remove("lazy");
             lazyImageObserver.unobserve(lazyImage);
            }
         });
        });

      lazyImages.forEach(function(lazyImage) {
       lazyImageObserver.observe(lazyImage);
      });
   } else {
     // Not supported, load all images immediately
    lazyImages.forEach(function(image){
        image.nextElementSibling.src = image.nextElementSibling.dataset.srcset;
      });
    }
  });

One last thought is that if you change the screen width back and forth, the image files are repeatedly downloaded again. If I could tie the above method in to a cache check then this would be golden...

查看更多
登录 后发表回答