verlok / vanilla-lazyload

LazyLoad is a lightweight, flexible script that speeds up your website by deferring the loading of your below-the-fold images, backgrounds, videos, iframes and scripts to when they will enter the viewport. Written in plain "vanilla" JavaScript, it leverages IntersectionObserver, supports responsive images and enables native lazy loading.
https://www.andreaverlicchi.eu/vanilla-lazyload/
MIT License
7.67k stars 675 forks source link

to_webp use cases? #288

Closed equinusocio closed 5 years ago

equinusocio commented 5 years ago

Hi, the to_webp flag is not so usefull since the <picture> tag is made also for that:

<picture>
  <source type="image/webp" srcset="flower.webp">
  <source type="image/jpeg" srcset="flower.jpg">
  <img src="fallback.jpg">
</picture>

It will try to load the .webp if supported, if not it will try with .jpg. If the picture tag isn't supported at all old browsers will show the <img> with the default src...

I can't figure out when using it.. maybe it can be removed to optimise the code?

verlok commented 5 years ago

Hi and thank you for opening this.

The to_webp option in fact is used by many, since the picture tag is the most verbous one when you need to do responsive images and all your image variants have the width/height ratio.

For brevity, and for performance optimization, it is much better to use the srcset and sized attributes of the img tag.

If you wanted to have responsive images using the picture tag you are, of course, free to do it, but please consider the length of the HTML portion which will be required to just render a single image.

verlok commented 5 years ago

If you want to see use cases of to_webp please take a look at the demos folder. Try and get the same result using the picture tag and see the difference.

equinusocio commented 5 years ago

Sorry @verlok i don't get the point. Since i can add both media and srcset to the source elements, why not just preload the <picture> or the img inside it? By this way i just need to declare my semantic html with the resources i need.

<picture>
  <source type="image/webp" media="(min-width: 650px)"  srcset="flower.webp">
  <source type="image/jpeg" media="(min-width: 465px)" srcset="flower.jpg">
  <img src="fallback.jpg">
</picture>

I just think that to_webp is something outside the scope of this library that should only allow you to preload images, not manipulating them. Also this option disable the native fallback functionality provided by the html (above snippet). Yes html is prolix, but, you know, you can't just try to "simulate" their functionalities with js.

verlok commented 5 years ago

@equinusocio thanks for your reply.

I think your HTML example is very short since you have only 2 images: one webp and one jpg. Consider the following html:

<img
    alt="Sneakers &amp; Tennis shoes basse"
    data-src="img/image_220.jpg"
    data-srcset="img/image_220.jpg 220w, img/image_330.jpg 330w, img/image_440.jpg 440w, img/image_550.jpg 550w, img/image_660.jpg 660w"
    data-sizes="(min-width: 768px) 330px, (min-width: 1024px) 220px, 440px"
/>

This provides 5 images URLs in the srcset that the browser could choose to download depending on the match between the sizes attribute and the width resolved by the browser matching viewport width and the screen density.

To obtain the same result using the picture tag, and supporting the webp format, you would need to do write tons of lines of source tags.

verlok commented 5 years ago

I figured out the picture tag would need to be something like this:

<picture>
    <source type="image/jpeg" media="" data-srcset="img/image_440.jpg 1x, img/image_660.jpg 2x">
    <source type="image/jpeg" media="(min-width: 768px)" data-srcset="img/image_330.jpg 1x, img/image_660.jpg 2x">
    <source type="image/jpeg" media="(min-width: 1024px)" data-srcset="img/image_220.jpg 1x, img/image_440.jpg 2x">
    <source type="image/webp" media="" data-srcset="img/image_440.jpg 1x, img/image_660.jpg 2x">
    <source type="image/webp" media="(min-width: 768px)" data-srcset="img/image_330.jpg 1x, img/image_660.jpg 2x">
    <source type="image/webp" media="(min-width: 1024px)" data-srcset="img/image_220.jpg 1x, img/image_440.jpg 2x">
    <img alt="Sneakers &amp; Tennis shoes basse" data-src="img/image_220.jpg">
</picture>

And this only for one image serving 3 media queries and 5 image sizes.

The weight of the HTML would become much heavier the more media queries and image sizes you want to serve.

Imagine this multiplied for all the images in a product listing page.

equinusocio commented 5 years ago

I get your point, i just don't like the to_webp api because i think it's a bit out of the lazyloading scope. Btw your (complex) example can be reduced to something like this:

<picture>
  <source type="image/webp" srcset="image_440.webp 440w, image_660.webp 660w" sizes="(min-width: 768px) 330px, (min-width: 1024px) 220px, 440px" >
  <source type="image/jpeg" srcset="image_440.jpg 440w, image_660.jpg 660w" sizes="(min-width: 768px) 330px, (min-width: 1024px) 220px, 440px" >

  <img src="img/image_220.jpg" srcset="..." sizes="...">
</picture>

Where (extension free):

Combining these rules the browser will load the bigger file based on the screen density, but it will change the intrisic size (rendered sizes) of the image based on the media queries.


Why not considering the Lazysizes approach where it simply support the native picture (so to_web is a meh)?

<picture>

  <source data-srcset="500.jpg" media="(max-width: 500px)" />
  <source data-srcset="1024.jpg" media="(max-width: 1024px)" />
  <source data-srcset="1200.jpg" />

  <img src="..." data-src="1024.jpg" class="lazyload" />
</picture>

My suggestion here is to keep this lib as "standard" as possibile removing "misleading" functionalities. I might be wrong so it's your decision :) Thanks for the replies.

verlok commented 5 years ago

Btw your (complex) example can be reduced to something like this:

  <source type="image/webp" srcset="image_440.webp 1x, image_660.webp 2x" sizes="(min-width: 768px) 330px, (min-width: 1024px) 220px, 440px" >

Oh my god, I didn't know that sizes attribute could be used in the source tag too. I'll definitely take a look into that. If it solves, lets be declarative in the HTML and leave lazyload out of this.

The thing you got wrong, though, is that you used the x descriptor in the srcset, in conjunction with the sizes attribute. Check this guide. It should be like this:

  <source type="image/webp" srcset="img/image_220.jpg 220w, img/image_330.jpg 330w, img/image_440.jpg 440w, img/image_550.jpg 550w, img/image_660.jpg 660w" sizes="(min-width: 768px) 330px, (min-width: 1024px) 220px, 440px" >

But I got your point.

Thanks for now.

verlok commented 5 years ago

I agree with you though, this goes beyond the purpose of lazyloading.

I'm gonna try if this works.

<picture>
  <source 
    type="image/webp"
    srcset="img/image_220.webp 220w, 
      img/image_330.webp 330w, 
      img/image_440.webp 440w, 
      img/image_550.webp 550w, 
      img/image_660.webp 660w"
    sizes="(min-width: 768px) 330px, (min-width: 1024px) 220px, 440px"
  >
  <source
    type="image/jpeg"
    srcset="img/image_220.jpg 220w, 
      img/image_330.jpg 330w, 
      img/image_440.jpg 440w, 
      img/image_550.jpg 550w, 
      img/image_660.jpg 660w"
    sizes="(min-width: 768px) 330px, (min-width: 1024px) 220px, 440px"
  />
  <img src="img/image_220.jpg" alt="Sneakers &amp; Tennis shoes">
</picture>

If it does, I might consider deprecating the to_webp option.

equinusocio commented 5 years ago

The thing you got wrong, though, is that you used the x descriptor in the srcset, in conjunction with the sizes attribute.

~Nope :) srcset can contains w or x descriptors. From the spec~

~Zero or one of the following:~ ~- A width descriptor, consisting of: ASCII whitespace, a valid non-negative integer giving a number greater than zero representing the width descriptor value, and a U+0077 LATIN SMALL LETTER W character.~ ~- A pixel density descriptor, consisting of: ASCII whitespace, a valid floating-point number giving a number greater than zero representing the pixel density descriptor value, and a U+0078 LATIN SMALL LETTER X character.~

EDIT: You're right. I missed this line:

If an element has a sizes attribute present, all image candidate strings for that element must have the width descriptor specified.

verlok commented 5 years ago

Hey! I've tried the following HTML document in different browser (not on Safari yet), and it works great on every browser. Opera, Chrome and Firefox download the fake webp files, MS Edge the jpeg files, IE the fallback one.

<html>
    <head>
        <title>Picture!</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <style>
            img {
                width: 440px;
                height: auto;
            }
            @media (min-width: 768px) {
                img {
                    width: 330px;
                }
            }
            @media (min-width: 1024px) {
                img {
                    width: 220px;
                }
            }
        </style>
    </head>
    <body>
        <picture>
            <source
                type="image/webp"
                srcset="
                    https://placehold.it/220?text=220.webp 220w,
                    https://placehold.it/330?text=330.webp 330w,
                    https://placehold.it/440?text=440.webp 440w,
                    https://placehold.it/660?text=660.webp 660w,
                    https://placehold.it/880?text=880.webp 880w
                "
                sizes="(min-width: 1024px) 220px, (min-width: 768px) 330px, 440px"
            />
            <source
                type="image/jpeg"
                srcset="
                    https://placehold.it/220?text=220.jpg 220w,
                    https://placehold.it/330?text=330.jpg 330w,
                    https://placehold.it/440?text=440.jpg 440w,
                    https://placehold.it/660?text=660.jpg 660w,
                    https://placehold.it/880?text=880.jpg 880w
                "
                sizes="(min-width: 1024px) 220px, (min-width: 768px) 330px, 440px"
            />
            <img
                src="https://placehold.it/440?text=440.jpg"
                alt="Sneakers &amp; Tennis shoes"
            />
        </picture>
    </body>
</html>
verlok commented 5 years ago

The only strange thing is that my version of MS Edge (which is quite old) downloads the wrong size (660w) of the image, but the others do good.

verlok commented 5 years ago

I'm wondering if one could put everything inside the img tag except the webp version, which could go in the source tag.

So long story short, this:

<html>
    <head>
        <title>Picture!</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <style>
            img {
                width: 440px;
                height: auto;
            }
            @media (min-width: 768px) {
                img {
                    width: 330px;
                }
            }
            @media (min-width: 1024px) {
                img {
                    width: 220px;
                }
            }
        </style>
    </head>
    <body>
        <picture>
            <source
                type="image/webp"
                srcset="
                    https://placehold.it/220?text=220.webp 220w,
                    https://placehold.it/330?text=330.webp 330w,
                    https://placehold.it/440?text=440.webp 440w,
                    https://placehold.it/660?text=660.webp 660w,
                    https://placehold.it/880?text=880.webp 880w
                "
                sizes="(min-width: 1024px) 220px, (min-width: 768px) 330px, 440px"
            />
            <img
                src="https://placehold.it/440?text=440.jpg"
                srcset="
                    https://placehold.it/220?text=220.jpg 220w,
                    https://placehold.it/330?text=330.jpg 330w,
                    https://placehold.it/440?text=440.jpg 440w,
                    https://placehold.it/660?text=660.jpg 660w,
                    https://placehold.it/880?text=880.jpg 880w
                "
                sizes="(min-width: 1024px) 220px, (min-width: 768px) 330px, 440px"
                alt="Sneakers &amp; Tennis shoes"
            />
        </picture>
    </body>
</html>
verlok commented 5 years ago

Yes! It still works. It's the shortest version of the markup if one needs to have responsive images. I think I'll drop the to_webp option then. Let me sleep over it. Update tomorrow (GMT+1).

verlok commented 5 years ago

It also already works with lazyload, no need to do anything else.

<html>
    <head>
        <title>Picture!</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <style>
            img {
                width: 440px;
                min-height: 220px;
                height: auto;
            }
            @media (min-width: 768px) {
                img {
                    width: 330px;
                }
            }
            @media (min-width: 1024px) {
                img {
                    width: 220px;
                }
            }
        </style>
    </head>
    <body>
        <picture>
            <source
                type="image/webp"
                data-srcset="
                    https://placehold.it/220?text=220.webp 220w,
                    https://placehold.it/330?text=330.webp 330w,
                    https://placehold.it/440?text=440.webp 440w,
                    https://placehold.it/660?text=660.webp 660w,
                    https://placehold.it/880?text=880.webp 880w
                "
                sizes="(min-width: 1024px) 220px, (min-width: 768px) 330px, 440px"
            />
            <img
                data-src="https://placehold.it/440?text=440.jpg"
                data-srcset="
                    https://placehold.it/220?text=220.jpg 220w,
                    https://placehold.it/330?text=330.jpg 330w,
                    https://placehold.it/440?text=440.jpg 440w,
                    https://placehold.it/660?text=660.jpg 660w,
                    https://placehold.it/880?text=880.jpg 880w
                "
                data-sizes="(min-width: 1024px) 220px, (min-width: 768px) 330px, 440px"
                alt="Sneakers &amp; Tennis shoes"
            />
        </picture>
        <script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@10.20.0/dist/lazyload.min.js"></script>
        <script>
            var ll = new LazyLoad();
        </script>
    </body>
</html>
verlok commented 5 years ago

I'm going to pick the code in my picture gist and place it in a new demo in the demos folder. I already released version 10.20.1 which fixed a bug with the previous code (data-sizes -> sizes in the source tag), so now it's working properly and to_webp became quite useless indeed. Cheers.

equinusocio commented 5 years ago

@verlok Nice! :)

ipernet commented 5 years ago

Should we clean everything related to webp (webp detection support, to_webp) to clean up a bit then bump the version number to a major one?

verlok commented 5 years ago

@ipernet Sounds good. Could be. way to clean up and drop the support to version 8.x at once (see #306)

verlok commented 5 years ago

I've just noticed in the MDN documentation for the source tag that the sizes and srcset attributes are experimental and should not be used in production. Even if in my demo, everything works fine in every browser.

I guess that I cannot ask LazyLoad users to use the above discussed markup to serve WebP images only where supported. So maybe for now the to_webp option still makes sense?

@equinusocio

verlok commented 5 years ago

The W3C spec for the source tag doesn't mention anything about those attributes being experimental, though. 🤔

verlok commented 5 years ago

See demos/webp_native.html for HTML, live demo here https://www.andreaverlicchi.eu/lazyload/demos/webp_native.html

equinusocio commented 5 years ago

The source tag and relative attributes are part of the living standard. They are not experimental anymore. Many MDN pages need to be updated (anyone can do it easily)

https://html.spec.whatwg.org/multipage/embedded-content.html#the-source-element

I will update the mdn page.

EDIT: @verlok Done. The experimental flags are gone from the page content. We need to wait the PR approvation for the browsers compatibility table. Here the PR https://github.com/mdn/browser-compat-data/pull/3479

verlok commented 5 years ago

Great! Thanks. Will proceed as @ipernet suggested then.

verlok commented 5 years ago

310 closes this I guess.

teq-91 commented 5 years ago

please bring it back! what about background images? you missed them entirely with your discussion.

equinusocio commented 5 years ago

@hyteq As I said, I think is outside the lazy load scope. You can follow multiple ways to load web as background image with CSS. One of them is described here: https://css-tricks.com/using-webp-images/

teq-91 commented 5 years ago

@hyteq As I said, I think is outside the lazy load scope. You can follow multiple ways to load web as background image with CSS. One of them is described here: https://css-tricks.com/using-webp-images/

this does not help at all. your mentioned solution has three disadvantages:

  1. it requires another 3rd party package with a little overhead
  2. its not possible to store the image url inside of the html tag element. i need to use a css file at all for setting all the background images for my entire website
  3. the 2nd point brings much larger css files that are not needed anyway. this produces much more traffic as well. even google's pagespeed tells you to reduce unused css rules, so this will reduce your entire seo rating on google

lets said again: please bring it back!

it doesnt matter if its outside of the scope of lazyloading. its quite a feature and nothing else. dont just remove it when somebody is not happy about it.

i agree, there is another better way to give the part of loading webp images to the browser using the picture tag. but dont remove such a feature unless there is no equivalent way to do so with background-images. the mentioned option above is it not.

EDIT:

btw.: so far as i know, it's not supported to use this lazyloader with background-images that are defined inside of css files. what i want to say is: your solution does not work at all together with this lazyloader while it requires a data-src="url(...)".

equinusocio commented 5 years ago

There is not standard ways to lazyload assets from CSS. The only way you can do that is loading the relative (scoped) CSS only when the HTML is loaded, for example using one of the current component-based frameworks like Vue, React, Svelte, Angular.

Additionally you can use postcss to convert background to webp using https://github.com/ai/webp-in-css.

Anothe options is avoid using background images and use standard <img> with object-[fit | position] property, and lazy load them with this lib, here an example https://jsfiddle.net/g6tou2q0/2/

Btw, I'm not the author of this library.