Closed equinusocio closed 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.
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.
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.
@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 & 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.
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 & 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.
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):
or
load image_660 on 2x(min-width: 768px)
show the image at 330px (intrinsic size)(min-width: 1024px)
show the image 220px (intrinsic size)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.
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.
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 & Tennis shoes">
</picture>
If it does, I might consider deprecating the to_webp
option.
The thing you got wrong, though, is that you used the
x
descriptor in thesrcset
, in conjunction with thesizes
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.
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 & Tennis shoes"
/>
</picture>
</body>
</html>
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.
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 & Tennis shoes"
/>
</picture>
</body>
</html>
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).
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 & 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>
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.
@verlok Nice! :)
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?
@ipernet Sounds good. Could be. way to clean up and drop the support to version 8.x at once (see #306)
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
The W3C spec for the source tag doesn't mention anything about those attributes being experimental, though. 🤔
See demos/webp_native.html
for HTML, live demo here https://www.andreaverlicchi.eu/lazyload/demos/webp_native.html
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
Great! Thanks. Will proceed as @ipernet suggested then.
please bring it back! what about background images? you missed them entirely with your discussion.
@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/
@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:
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(...)".
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.
Hi, the
to_webp
flag is not so usefull since the<picture>
tag is made also for that: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?