I'm running a page through Google's Page Speed Analyzer and for mobile, it's telling me I should download the proper size images. I know the viewport size, but I don't know the width of the slot because we are using a responsive layout. I've looked at the documentation and a bunch of examples and none of them are using what I think should be the correct image.
The Network tab of developer tools is showing we are downloading about.webp for mobile and not either of the smaller images (200px or 400px). Even when changing the device in Chrome to a smaller viewport width, it still downloads about.webp.
What is the correct HTML for a picture element to get Page Speed Analyzer to use the appropriate image?
Below is my picture element. Thanks!
<div class="row"><div class="col-md-3">
<picture>
<source type="image/webp"
sizes="(max-width: 200px) 200px,
(max-width: 400px) 400px,
(max-width: 1500px) 1500px"
srcset="//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w200.webp 200w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w400.webp 400w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt.webp 1500w">
<source type="image/png"
sizes="(max-width: 200px) 200px,
(max-width: 400px) 400px,
(max-width: 1500px) 1500px"
srcset="//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w200.png 200w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w400.png 400w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt.png 1500w">
<img src="//cdn.storyboardthat.com/site-images/articles/education/about-sbt.png"
alt="Storyboard That"
title="Storyboard That"
class="lazyload "
style="max-width:100%;height:auto;"
width="1500" height="400" loading="lazy">
</picture>
</div>
You have forgotten about Device Pixel Ratio (DPR).
You see the <picture> element basically says to browsers "here are a few options to choose from, I have indicated my preference but it is up to you to decide which image you think is best".
As you haven't specified a DPR preference the browser is using the following logic:
"Ok so I have 3 images to choose from, what is my current DPR? Oh I have DPR set to 3.
How big is the image at this screen width (320px)? Full screen width so I need a 320px image.
Ok so it is 320px * 3 DPR to ensure I have the highest quality image that matches my current display resolution.
So I need a 960px image minimum. My options are 200, 400 or 1500, I better choose the 1500 image and down sample it."
So how do I control pixel density decisions?
You can specify pixel density with 1x, 2x, 4x etc. For example:
<img src="default.webp"
srcset="hiddef.webp 2x, heighestdef.webp 4x"
alt="Image description">
However with how you have currently structured your picture element it won't quite work as you can't combine image 200w with image 2x to do image 200w 2x.
If you want to keep the same image at all DPIs then you would specify that image 3 times.
<img src="default.webp"
srcset="default.webp 2x, default.webp 4x"
alt="Image description">
Otherwise if you want the browser to change the image automatically based on DPR just leave your <picture> element as it is.
I see that the code snippet you have used is incorrect, refer this:
<picture>
<source media="(max-width:200px)" srcset="about-w200.webp">
<source media="(max-width:400px)" srcset="about-w400.webp">
<img src="about-sbt.webp alt="About" style="width:auto;">
</picture>
A working reference link
Related
So I have this code of a responsive image using scrset:
<picture class="background-image">
<source type="image/jpg"
srcset="http://localhost:61186/public/images/asia_1637411941_1920x1280.jpg 1920w,
http://localhost:61186/public/images/asia_1637411941_1720x1147.jpg 1720w,
http://localhost:61186/public/images/asia_1637411941_1520x1013.jpg 1520w,
http://localhost:61186/public/images/asia_1637411941_1320x880.jpg 1320w,
http://localhost:61186/public/images/asia_1637411941_1120x747.jpg 1120w,
http://localhost:61186/public/images/asia_1637411941_920x613.jpg 920w,
http://localhost:61186/public/images/asia_1637411941_720x480.jpg 720w,
http://localhost:61186/public/images/asia_1637411941_520x347.jpg 520w,
http://localhost:61186/public/images/asia_1637411941_320x213.jpg 320w">
<img src="http://localhost:61186/asia_1637411941_1920x1280.jpg" alt="Asia" />
</picture>
This (with surrounding html) renders to this on a 360px width screen:
Now, the image element is around 100px width. But Chrome loads the 1120px version (no cache / incognito tab)
Now ideally it would load the 320px width image as the element is just 100px width. But if that is not the case then based on the viewport of 360px I would at max expect it to pick the 520px version of the image. But it doesn't.
Now in Chrome Lighthouse I get a score penalty because I need to "properly size the images". But I feel like I'm doing that. Anyone has a solution?
If you run your markup through the W3 Validator you get
Error: When the srcset attribute has any image candidate string with a width descriptor, the sizes attribute must also be present.
I don't know the layout you're looking for and hence what media queries to use, but see if the following quick fix works for you:
<picture class="background-image">
<source type="image/jpg"
srcset="http://localhost:61186/public/images/asia_1637411941_1920x1280.jpg 1920w,
http://localhost:61186/public/images/asia_1637411941_1720x1147.jpg 1720w,
http://localhost:61186/public/images/asia_1637411941_1520x1013.jpg 1520w,
http://localhost:61186/public/images/asia_1637411941_1320x880.jpg 1320w,
http://localhost:61186/public/images/asia_1637411941_1120x747.jpg 1120w,
http://localhost:61186/public/images/asia_1637411941_920x613.jpg 920w,
http://localhost:61186/public/images/asia_1637411941_720x480.jpg 720w,
http://localhost:61186/public/images/asia_1637411941_520x347.jpg 520w,
http://localhost:61186/public/images/asia_1637411941_320x213.jpg 320w">
sizes="100vw">
<img src="http://localhost:61186/asia_1637411941_1920x1280.jpg" alt="Asia" />
</picture>
I have been into CSS for quite a while now, but srcset and sizes for the image element confuse me. Here is an example that I thought would work.
<img alt="Background image flowers"
srcset="img/flowers-480.jpg 480w,
img/flowers-480#2x.jpg 480w 2x,
img/flowers-768.jpg 768w,
img/flowers-768#2x.jpg 768w 2x,
img/flowers-960.jpg 960w,
img/flowers-960#2x.jpg 960w 2x,
img/flowers-1280.jpg 1280w,
img/flowers-1280#2x.jpg 1280w 2x"
sizes="(max-width: 1279px) 100vw,
(min-width: 1280) 1280px"
src="img/flowers-960.jpg">
The idea is to have an image that's 100% of the viewport until the viewport is 1280px wide or wider, then the image will be fixed size. However, to compensate for higher DPI devices I thought it was recommended to add DPI descriptors (1.5x, 2x and so on), as suggested here and here.
What I thought the above code would do is:
check sizes, see what kind of size to expect for the image (and if a relative unit is given such as % or vw, calculate the pixel width)
find the images in srcset width that is closest to that width
from these images, filter out the one with a DPI descriptor closest to the device's DPI
However, when I put this through a validator I get the following error:
Error: Bad value for attribute srcset on element img: Width for image img/flowers-480#2x.jpg is identical to width for image
img/flowers-480.jpg
So clearly I am completely missing the point of how srcset and sizes work. What am I doing wrong?
As defined on MDN for <img srcset="...">:
Each string is composed of:
a URL to an image, optionally, whitespace followed by one of:
a width descriptor, or a positive integer directly
followed by 'w'. The width descriptor is divided by the source size
given in the sizes attribute to calculate the effective pixel density.
a pixel density descriptor, which is a positive floating point number
directly followed by 'x'.
You tried to use both, and that's illegal.
For a combination of HPDI settings and responsive sizes for images you should actually use <picture> with a few <source> and a fallback <img> elements.
Details are in this article.
According to MDN, "It is incorrect to mix width descriptors and pixel density descriptors in the same srcset attribute. Duplicate descriptors (for instance, two sources in the same srcset which are both described with '2x') are invalid, too."
You have 2x listed 4 times. That's invalid.
Here is an example from MDN:
Example 4: Using the srcset and sizes attributes
The src attribute is ignored in user agents that support srcset when using 'w' descriptors. When the (max-width: 600px) media condition matches, the image will be 200px wide, otherwise it will be 50vw wide (50% of the viewport width).
<img src="clock-demo-thumb-200.png"
alt="Clock"
srcset="clock-demo-thumb-200.png 200w,
clock-demo-thumb-400.png 400w"
sizes="(max-width: 600px) 200px, 50vw">
The "art direction use case", linked in #vbarinov's answer, quoted from developers.google.com, shows how to combine both width and pixel density as criteria for which image source to load and display:
<picture>
<source media="(min-width: 800px)" srcset="head.jpg, head-2x.jpg 2x">
<source media="(min-width: 450px)" srcset="head-small.jpg, head-small-2x.jpg 2x">
<img src="head-fb.jpg" srcset="head-fb-2x.jpg 2x" alt="a head carved out of wood">
</picture>
In viewports that are at least 800 pixels wide, head.jpg should be displayed on a single density display, while head-2x.jpg should be displayed on a display with double or greater density.
Viewports below 800 pixels with, but at least 450 pixels wide, single-density displays should show head-small.jpg, while head-small-2x.jpg is for double density or greater.
The fallback image definition inside the <img> tag has another srcset that defines a double-density version (head-fb-2x.jpg) for viewports below 450 pixels width.
Single-density devices and browsers that don't support source sets or picture elements will fall back to display head-fb.jpg.
In a more complex scenario, we can also add alternative image formats, to take advantage of webp compression while providing jpg versions for browsers without webp support.
<picture>
<source media="(min-width: 800px)" srcset="head.webp, head-2x.webp 2x" with="800" height="941">
<source media="(min-width: 800px)" srcset="head.jpg, head-2x.jpg 2x" with="800" height="941">
<source media="(min-width: 450px)" srcset="head-small.webp, head-small-2x.webp 2x" width="800" height="941">
<source media="(min-width: 450px)" srcset="head-small.jpg, head-small-2x.jpg 2x" width="800" height="941">
<img src="head-fb.jpg" srcset="head-fb-2x.jpg 2x" width="400" height="471" alt="a head carved out of wood">
</picture>
Here is a codepen of the complex example.
As of 2022, I'd argue it's just easier to assume high DPI screens and leverage the breakpoint idea of srcset:
<img alt="Background image flowers"
srcset="img/flowers-480.jpg 480w,
img/flowers-768.jpg 768w,
img/flowers-960.jpg 960w,
img/flowers-1280.jpg 1280w,
img/flowers-1920.jpg 1920w,
img/flowers-4096.jpg 4096w"
sizes="(max-width: 1279px) 100vw,
(min-width: 1280) 1280px"
src="img/flowers-960.jpg">
I'm building a responsive Wordpress website. The blog feed has a list of stories which have a title and an image. The image sizes should be as follows:
up to 479px wide - 140x140px (stories are displayed in a list)
over 480px wide - 360x190px (stories are displayed in a row of 3)
I've been trying to use SRCSET for this so that the 140x140px image would be loaded for browsers up to 479px and the 360px image would be loaded for browsers 480px and over.
I've googled and read literally every bit of documentation out there on sizes and srcset but I just can't get my head around it. So far I've come up with the following:
<img
src="http://placekitten.com/140/140"
srcset="
http://placekitten.com/140/140 140w,
http://placekitten.com/360/190 360w"
sizes="
(max-width: 479px) 140px,
(min-width: 480px) 360px,
100vw"
alt=""
class="lazyload"
/>
Unfortunately all this does is display the 360x190px at every width, despite the actual src of the image being set to the 140x140px image.
Can anyone see what I've missed? I think it's the sizes that I'm most confused about. I added in media queries like documented but they don't seem to be applied?
Thank you!
Note that this srcset strategy on the <img> element depends on the fact that the browser has not cached the image yet. This strategy is meant to save the bandwidth for the browser. So, if you start off with a wide viewport, the browser simply fetches the larger of the two images and will no longer fallback to the smaller one even if you resize.
If you want to force the browser to load images at various viewport breakpoints, use <picture> instead:
<picture alt="" class="lazyload">
<source srcset="https://via.placeholder.com/360x190" media="(min-width: 480px)" />
<img src="https://via.placeholder.com/140x140" />
</picture>
Check out the code below.
<picture>
<source class="img-fluid" srcset="//www.fillmurray.com/140/140" media="(max-width: 479px)"><img class="img-fluid" src="//www.fillmurray.com/360/190">
</picture>
I have the following
<img
src="/img/footer/logo_white.png?v=2.0"
srcset="/img/footer/logo_white.png?v=2.0 1x,
/img/footer/logo_white2x.png?v=2.0 2x"
>
which works fine on normal and hiDPI screens.
But when the viewport's very small (below 400px) the logo doesn't fit therefore I need a smaller version of the image in terms of real length, which I created. Then I tried
<img
class="biw-logo"
sizes="(max-width: 390px) 110px, 175px"
src="/img/footer/biw_logo.png?v=2.0"
srcset="/img/footer/biw_logo_small.png?v=2.0 110w,
/img/footer/biw_logo.png?v=2.0 175w,
/img/footer/biw_logo2x.png?v=2.0 350w"
>
Which works in terms of showing the _small image for viewports lower than 390 pixels - but now I've lost the "high resolution" factor; I cannot force the iOS browser in iphone5s to display a 220px image in length of 110px with the above syntax.
Could you correct my syntax?
<img class="biw-logo" sizes="(max-width: 390px) 110px, 175px" src="http://placehold.it/175x75" srcset="http://placehold.it/110x50 110w,
http://placehold.it/175x75 175w, http://placehold.it/350x150 350w">
You can do that with srcset and sizes. At first tell the browser which images you have available and how many pixels wide these images are, this can be done with srcset:
<img srcset="
/img/footer/logo_white.png?v=2.0 300w,
/img/footer/logo_white2x.png?v=2.0 600w,
/img/footer/logo_white_small.png?v=2.0 150w
">
Now the browser knows it can select from three images that are 150, 300 and 600 pixels wide (I guessed the dimensions, your actual widths might be different).
Second, you tell the browser how large the image will be displayed in the webpage, this can be achieved with sizes:
<img
sizes="(max-width: 400px) 150px, 300px"
srcset="..."
>
The browser knows now, that if the width of the viewport is 400px or less the image will be displayed 150px wide, for viewports larger than 400px it is displayed 300px wide.
This is enough information for the browser to select the right image. On a normal desktop with a normal screen it will select the 300w-image and on a HiDPI desktop it will be the 600w one. On a small viewport with a normal screen the 150w will get selected and on a small viewport with HiDPI the 300w one.
If you want more information about srcset and sizes, take a look at http://ericportis.com/posts/2014/srcset-sizes/.
You also can try using a couple more sources like that:
<picture>
<source srcset="img.png" media="(resolution: 150dpi)" type="image/png" />
<source srcset="img2x.png" media="(resolution: 300dpi)" type="image/png" />
<img src="img.png" alt="alt text" />
</picture>
Or something like that - i didn't test it, i need to learn more about resolution media query to be sure.
I'm having trouble understanding how to keep srcset from loading any images on screens < 768px.
I've tried the code below but the sizes attribute doesn't seem to do what you may think.
Below loads 1024.jpg on all screen sizes:
<img
src="default.jpg"
srcset="img/1024.jpg 1024w"
sizes="(min-width: 768px) 768px, 100vw"
/>
Or the picture element, if it would honor an empty srcset but it only "hints" to which image a browser should load.
The other answer isn't really satisfying. In general with srcset you give the browser the choice to select an image candidate. While you can assume which image is taken on certain devices. You don't have any control. Each image in srcset can be taken.
So if you want to control, what is used or not used, you need to use the picture element.
Here are 3 examples. If the viewport is 768px or wider the 'img/1024.jpg' image is downloaded, otherwise a data uri or a broken img is selected.
<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source srcset="img/1024.jpg" media="(min-width: 768px)">
<!--[if IE 9]></video><![endif]-->
<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Image">
</picture>
<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source srcset="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" media="(max-width: 768px)">
<!--[if IE 9]></video><![endif]-->
<img src="img/1024.jpg" alt="Image">
</picture>
<!-- you can also write (but this makes it invalid) -->
<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source srcset="img/1024.jpg" media="(min-width: 768px)">
<!--[if IE 9]></video><![endif]-->
<img alt="Image">
</picture>
Although the first and the second code example are absolutely valid. There is currently some discussion to allow this by simply omitting the srcset (see code example 2). See this discussion here: https://github.com/ResponsiveImagesCG/picture-element/issues/243
There's a really simple solution to this which works without <picture>.
It is likely not intended to be used this way, but it works very well.
Attach a 1x1px image and it will be used whenever sizes == 0vw. In this case this would be true for everything under 768px:
<img
src="default.jpg"
srcset="img/null.jpg 1w
img/1024.jpg 1024w"
sizes="(max-width: 768px) 0vw, 100vw"
/>
I'm aware that this is not answering the OP's question of "loading NO images", but it's relatively close and you can reference the same 1x1 px image in all other situations, which means it's not gonna be downloaded there.
One caveat:
In the following situation I believe the browser might decide to use the 1x1px image already from 512px and downwards even though we specify 100px
<img
src="default.jpg"
srcset="img/null.jpg 1w
img/1024.jpg 1024w"
sizes="(max-width: 100px) 0vw, 100vw"
/>
This is due to the fact that the browser takes whatever is closest to the desired size in this case 1 is closer to 511 than 1024 is... not sure though
Edit: This situation could quite easily be fixed by attaching a 200w image as well, which you would likely do in any case
EDIT:
To say it simply, you can't.
Removing/hiding an image element must be done in CSS with media queries, or with Javascript.
The srcset and sizes tags are useful for choosing the content source of an image element, but it cannot control the existence or visibility of the element.
The srcset and sizes tags are intended to augment responsive CSS. Their values should follow whatever breakpoints are defined in CSS.
srcset
srcset is a list of available images the browser can choose from with their respective widths.
According to the latest spec, it will choose only from that list when populated:
For backwards compatibility, one of the URLs is specified in the img element's src attribute. In new user agents, the src attribute is ignored when the srcset attribute uses w descriptors.
Therefore, it sees 1024.jpg as the only choice and ignores default.jpg.
Add the default image to srcset (with the correct w descriptor - here I assume default.jpg is 768px wide):
<img
src="default.jpg"
srcset="default.jpg 768w, img/1024.jpg 1024w"
sizes="(min-width: 768px) 768px, 100vw"
/>
sizes
sizes tells the browser how wide the image will be when a given media query is true. This helps the browser calculate which image to pick from srcset.
Currently, sizes="(min-width: 768px) 768px, 100vw" is telling the browser:
"The image will be 768px wide if the viewport is larger than 768px, otherwise the image will be full width when the viewport is less than 768px."
I assume you don't want to use a 1024px image when the viewport is less than 768px.
To hint at a small image when the viewport is less than 768px, use max-width: 768px instead:
<img
src="default.jpg"
srcset="default.jpg 768w, img/1024.jpg 1024w"
sizes="(max-width: 768px) 768px, 100vw"
/>