懒加载HTML5图片元素

47

我一直在寻找可靠的方法来懒加载图像,同时使用HTML5规范的<picture>。目前大多数解决方案/插件都依赖于使用data-属性。我可能错了,但似乎这种方法不适用于与<picture>一起使用。

我只是想被指引正确的方向。如果有人有正在使用的解决方案,我很想看看。谢谢!

以下是符合HTML5规范的标准标记:

<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
请展示一下你的标记,好吗?你所说的“懒加载”,是指“在视口中加载”吗?你找到了哪些方法,请发一个并展示如何应用于<pictures>(以及为什么那样不起作用)? - Bergi
是的,抱歉我做了这个假设。我所说的懒加载图片,指的是当它们在视口中时。我见过的最常见的图像解决方案是 jQuery 的 Lazy Load 插件 - MrRay
2
嗯,那个插件并不神奇。你可以很容易地将其调整为与图片中的<source>元素一起使用,而不是使用img的src属性。 - Bergi
1
@MrRay 我不知道浏览器如何解析(或将要解析)<picture>标签,但最有可能的是在html解析期间加载适当的图像(就像它们目前为<img>标签所做的那样),因此停止它的唯一方法是在<source>标签中使用data-src属性而不是src。但即使在这种情况下也存在一些问题,因此在LazyLoadXT中,我们开始使用<br>标签而不是<source>:http://ressio.github.io/lazy-load-xt/demo/picture.htm(我不记得`<source>标签存在什么问题,但在某些浏览器中无法正常工作,而<br>`可以)。 - Denis Ryabov
感谢@DenisRyabov。是的,这正是我要解决的问题。我看过这个插件——它是唯一一个解决这个问题的插件。现在我明白为什么它使用<br>标签了! - MrRay
FYI:<source>标签的问题在于旧浏览器不知道这个标签是自闭合的,并生成类似<picture><source><source><source></source></source></source></picture>的嵌套DOM,这样的结构会破坏插件的工作。也许可以修复这个问题,但更简单的方法是将<source>替换为所有浏览器都支持的<br> - Denis Ryabov
4个回答

80
现在是2020年2月,我很高兴地报告说,Google Chrome、Microsoft Edge(基于Chromium的Edge)和Mozilla Firefox都支持新的loading="lazy"属性。唯一现代浏览器的例外是苹果的Safari浏览器(包括iOS Safari和macOS Safari),但他们最近已经将其添加到Safari的代码库中,因此我预计它将在今年某个时候发布。 loading="lazy"属性仅适用于<img />元素(而不是<picture>),但请记住<picture>元素不代表实际的替换内容,<img />元素才是(即用户看到的图像始终由<img />元素呈现,<picture>元素只是意味着浏览器可以更改<img src="" />属性。根据2020年2月的HTML5规范(重点是我的):

picture元素与类似的videoaudio元素有些不同。虽然它们都包含source元素,但是当该元素嵌套在picture元素中时,source元素的src属性没有任何意义,并且资源选择算法也不同。而且,picture元素本身不显示任何东西;它仅为其包含的img元素提供上下文,使其能够从多个URL中进行选择。

所以这样做应该是可以正常工作的:
<picture>
    <source media="(min-width: 45em)" srcset="large.jpg" />
    <source media="(min-width: 18em)" srcset="med.jpg" />
    <source src="small.jpg" />
    <img src="small.jpg" alt="Photo of a turboencabulator" loading="lazy" />
</picture>

请注意, <picture> 元素 没有自己的 widthheight 属性;相反,应将 widthheight 属性应用于子元素 <source><img>

4.8.17 尺寸属性

在父级为 picture 元素时,imgiframeembedobjectvideosource 上的 widthheight 属性 [...] 可以指定元素的可视内容的尺寸(分别相对于输出介质的名义方向的宽度和高度),单位为 CSS 像素。

[...]

如果相关资源没有固有的宽度和高度,则必须省略这两个属性。

因此,如果您希望所有 <source> 图像呈现为 500px x 500px,则仅将尺寸应用于 <img> 元素(并不要忘记为视力受损的用户添加 alt="" 文本,在许多情况下甚至是法律要求):

<picture>
    <source media="(min-width: 45em)" srcset="large.jpg" />
    <source media="(min-width: 18em)" srcset="med.jpg" />
    <source src="small.jpg" />
    <img src="small.jpg" alt="Photo of a turboencabulator" loading="lazy" width="500" height="500" />
</picture>

1
@KyleMit 谢谢,我正在处理多个问题,我的意思是要链接到 https://dev59.com/L3E95IYBdhLWcg3wadOq - Ciro Santilli OurBigBook.com
这是否意味着 widthheight 属性也可以出现在 img 标签中? - Lucas David Ferrero

7

对于仍然感兴趣的人... 在重新审视这个问题后,我发现了一个相当新的脚本,叫做Lazysizes。它实际上非常灵活,但更重要的是,它允许我使用原帖中描述的HTML5标记进行图像的懒加载。

非常感谢这个脚本的创建者@aFarkas


5
使用 picture 元素和 intersection observer 实现图片的懒加载示例,已在 Chrome 和 Firefox 中测试。Safari 不支持 intersection observer,因此图片会立即加载,IE11 不支持 picture 元素,所以我们回退到默认的 img 元素。
媒体查询中的媒体属性是任意的,可以根据需要进行设置。
宽度阈值设置为 960px - 尝试在此宽度以上和以下重新加载页面,可以看到当图像滚动到视口中时下载中等(-m)或大型(-l)变化的图像。 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>

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

最后一个想法是,如果您反复更改屏幕宽度,图像文件将被重复下载。如果我能将上述方法与缓存检查相结合,那就太棒了...


3
在 web.dev 上,Google 建议可以使用 lazy 加载图片标签。
https://web.dev/browser-level-image-lazy-loading/

使用 图像元素定义的图像也可以进行懒加载:

<picture>
  <source media="(min-width: 800px)" srcset="large.jpg 1x, larger.jpg 2x">
  <img src="photo.jpg" loading="lazy">
</picture>

尽管浏览器会决定从哪个<picture>元素加载图像,但是loading属性只需要包含在后备元素中即可。

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