图片元素的懒加载

4
我正在寻求如何最佳地惰性加载图片元素的建议。我可能想使用一个小的jQuery辅助函数来确定图片是否“在屏幕上”。但是,我现在不确定如何进行srcset的惰性获取。所以,有什么想法吗?
以下是我正在使用的picture元素的示例。谢谢!
<picture alt="Random Celebrities" data-src="http://www.example.com/r/c_1,h_478,w_478/2015/03/19/random-celebrities-08-560x560.jpg">
    <!--[if IE 9]><video style="display: none;"><![endif]-->
    <source class='picture-source-1260' srcset='http://www.example.com/r/c_1,h_239,w_239/2015/03/19/random-celebrities-08-560x560.jpg, http://www.example.com/r/c_1,h_478,w_478/2015/03/19/random-celebrities-08-560x560.jpg 2x' media='(min-width: 1260px)'>
    <source class='picture-source-960' srcset='http://www.example.com/r/c_1,h_180,w_180/2015/03/19/random-celebrities-08-560x560.jpg, http://www.example.com/r/c_1,h_360,w_360/2015/03/19/random-celebrities-08-560x560.jpg 2x' media='(min-width: 960px)'>
    <source class='picture-source-760' srcset='http://www.example.com/r/c_1,h_150,w_150/2015/03/19/random-celebrities-08-560x560.jpg, http://www.example.com/r/c_1,h_300,w_300/2015/03/19/random-celebrities-08-560x560.jpg 2x' media='(min-width: 760px)'>
    <source class='picture-source-450' srcset='http://www.example.com/r/c_1,h_210,w_210/2015/03/19/random-celebrities-08-560x560.jpg, http://www.example.com/r/c_1,h_420,w_420/2015/03/19/random-celebrities-08-560x560.jpg 2x' media='(min-width: 450px)'>
    <source class='picture-source-320' srcset='http://www.example.com/r/c_1,h_160,w_160/2015/03/19/random-celebrities-08-560x560.jpg, http://www.example.com/r/c_1,h_320,w_320/2015/03/19/random-celebrities-08-560x560.jpg 2x'>
    <!--[if IE 9]></video><![endif]-->
    <noscript>
        <img class="picture-img-noscript" src="http://www.example.com/r/c_1,h_160,w_160/2015/03/19/random-celebrities-08-560x560.jpg" alt="Random Celebrities" />
    </noscript>
    <img class="picture-img" srcset="http://www.example.com/r/c_1,h_160,w_160/2015/03/19/random-celebrities-08-560x560.jpg" alt="Random Celebrities" />
</picture>

当我尝试为这个问题添加标签"picture"或"picture-element"时,它被转换为"image"。但是,为了澄清,我特别关注于使用picture标签和srcset定义的延迟加载图片。 - jerome
那么,一个在“折叠区域”之下或者其他方式不在视口内的图片元素(比如在走马灯里面被隐藏)只有在进入视口后才会被加载吗? - jerome
我使用了不正确的术语。我的意思是“懒加载”,即根据视口/设备仅加载必要的图像(列表中的图像)。图片元素规范没有指定浏览器如何加载视口外的图像,因此它们将在第一次渲染时加载。 - Adam Jenkins
明白了。那么为了澄清,对于任何遇到这个问题的人,我想要推迟实际图像的http请求,直到图片元素在视口中。谢谢! - jerome
但是你必须复制浏览器使用的确切逻辑(因为你需要通过JS确定要显示哪个img),那么你为什么还需要图片元素呢? - Adam Jenkins
显示剩余2条评论
3个回答

9
使用 lazysizes,它是一个高性能的图片懒加载工具,支持普通和响应式图片(包括picture元素)。

感谢您的回复@alexander。那么,我只需要在所有源标签中将srcset更改为data-srcset,并将类“lazyload”添加到img标签中吗?还有一个快速问题:lazysizes可以作为RequireJS的模块加载吗? - jerome
是的,没错,你可以使用requireJS来加载lazysizes。lazysizes本身正在使用未命名的umd模块模式。 - alexander farkas
@alexander还有一个问题。如果异步添加到DOM中的元素使用lazysizes会怎么样? - jerome
它会自动处理这些事情,不需要做任何操作。 - alexander farkas
6
看起来你是这个库的作者。根据 https://stackoverflow.com/help/promotion,请在你的回答中披露你的从属关系。 - Andy
显示剩余2条评论

3
我在这里创建了一个解决方案的笔: https://dev59.com/-mAg5IYBdhLWcg3wDHXS#54092875 codepen 已在Chrome和Firefox、Safari以及IE11中进行了测试(对于后者有回退方案)。
<!-- 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>

以下是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;
      });
    }
  });

@wp-overwatch.com,您能否详细说明一下?我刚刚查看了带有网络选项卡的Codpen,图片在进入视口之前被添加到som中-正如预期和描述的那样。 - Sean Doherty
如果您向下滚动到懒加载的底部图像,您会发现在DOM中有<img data-srcset="https://i.ibb.co/D5QyVPk/desert-l.jpg" alt="image" srcset="https://i.ibb.co/D5QyVPk/desert-l.jpg" class="fade-in">缺少有效的src属性。 图像仍然是懒加载的,因为源标记是正确的,但是如果您删除所有源标记,只保留img标记,则会注意到没有任何图像加载。 - hostingutilities.com
这是我所说的内容的一个例子。https://codepen.io/mrme44/pen/MWYXPxL - hostingutilities.com
2
经过详尽的寻找解决方案,我可以说上述代码仍然是我找到的唯一正确答案。根据我在2020年12月的测试,没有任何流行的惰性加载脚本/插件可靠地与图片标签一起使用或完全不起作用。@wp-overwatch.com 是正确的,该代码忽略了img标签,只涉及源标签。但是,发布的代码可以轻松扩展以包括picture标记内部的img标记的惰性加载。在本机浏览器惰性加载普及之前,这个解决方案仍然适用于完整的picture标记惰性加载。 - JD_dev
检查一下lazyImage.nextElementSibling.tagname是否为“IMG”,如果是的话,你可以给它一个“src”属性而不是“srcset”。但也许现代浏览器不介意带有srcset属性的<img>标签? - nydame
显示剩余5条评论

0

你其实不需要插件,只需在图片标签上加一个类,移除其中的“img”标签,并将src放入srcset中:

 <picture class="lazy">
   <source srcset='/images/image.jpg'/>
 </picture>

然后在 JavaScript 中,当你的 DOM 加载完成后:

获取所有“.lazy”节点:

const lazyImages = document.querySelectorAll('.lazy');

像这样调用一个函数:

 function lazyLoadImages() {
   lazyImages.forEach((val,i) => {

     let src = val.querySelector('source').getAttribute('srcset');
     let image = new Image();
     image.src = src;
     val.append(image);


   })
 }

请不要删除 <img> 标签 - <picture> 标签需要一个 <img> 来构成有效的 HTML,并在旧版浏览器上正确回退。 - Mike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接