6
votes

I'm trying to optimize a page which consists of a grid of images with breakpoints that makes it use 1, 2, 3 or 4 columns.

The html looks like this:

<img srcset=" image-300x200.jpg 300w, image-original.jpg 600w" sizes=" (min-width: 1px) calc( 100vw * (0.94 * 1.000) * 1.00 ), (min-width: 480px) calc( 100vw * (0.94 * 0.905) * 0.50 ), (min-width: 768px) calc( 100vw * (0.94 * 0.850) * 0.33 ), (min-width: 981px) calc( 100vw * (0.94 * 0.835) * 0.25 ), 280px" alt="E-Books customer service for Dutch Libraries" width="400" height="284">

The calc() functions may look a bit complicated but I've tested them with various viewport widths and then verified the size of the image in the browser and the math checks out.

For completeness, here's what it does:

[viewport width] - [94% container width] - [column gutters] / [nr of columns]

But whatever I try, Chrome (almost, yay for consistency) always selects the big image even when the size of the img on screen is well below 300px. I checked this in the developer tools > inspector > properties > img > currentsrc as pointed out in this answer

Can anyone help me?

1
This code by itself is not enough to replicate the problem. Can you provide a minimal reproducible example that demostrates the issue?Mr Lister
Sure i'll update the post as soon as i'm behind a computerREJH
Heh. Building the example code kind of set me on the track of finding the answer. Posted it below. Thanks for asking :DREJH

1 Answers

3
votes

I think I figured it out. it seems that the media queries don't work as I expected.

For example, with these rules:

sizes=" (min-width: 1px) ... (min-width: 480px) ... (min-width: 768px) ... "

one would assume that

  • Line #1 is true when screen > 1px wide
  • Line #2 is true when screen > 480px, overriding line #1
  • Line #3 is true when screen > 768px, overriding line #1 and #2

This is not how it works, at least not in practice. I think that the browser just looks for a rule that evaluates to true and calls it a day.

So it just goes:

  • Line #1 is true! Done! Applying the rule! Easy!

When I looked at my rules and the result with this logic in mind, it suddenly made sense that the browser insisted on using the biggest image because the calc() function I use for the first line is:

calc( 100vw * (0.94 * 1.0) * 1.0 ) - which is a complicated way of writing windowWidth * 0.94.

In other words, the browser assumes that the image is always 94% of the entire width of the window and doesn't apply any of the other calculations that take the breakpoints into account.

Anyway, changing the above rules to this:

sizes=" (min-width: 1px) and (max-width: 479px) ... (min-width: 480px) and (max-width: 767px) ... (min-width: 768px) and (max-width: 980px) ... "

makes sure that the rule only applies up to a certain point. Every time the next line evaluates as true, the other lines don't.

So this is what I went with in the end:

<img src="image-fallback.jpg" srcset=" image-300x200.jpg 300w, image-480x320.jpg 480w, image-600x400.jpg 600w, image-960x640.jpg 960w, image-1200x800.jpg 1200w" sizes=" (min-width: 1px) and (max-width: 479px) calc( 100vw * (0.94 * 1.000) * 1.00 ), (min-width: 480px) and (max-width: 767px) calc( 100vw * (0.94 * 0.905) * 0.50 ), (min-width: 768px) and (max-width: 980px) calc( 100vw * (0.94 * 0.850) * 0.33 ), (min-width: 981px) and (max-width: 1088px) calc( 100vw * (0.94 * 0.835) * 0.25 ), (min-width: 1089px) calc( 1089px * (0.94 * 0.835) * 0.25 ), 280px" />

And here's a working example of the above: Image srcset example (working)

Note: once the browser has loaded a larger image, it's reluctant to load smaller ones. That's what the 'force refresh' button is for.

Note #2: For debugging I added more source images compared to my original question. I thought it would be easier to keep it simple with two sizes but I think I would have never figured this out if I hadn't added a bunch. When I only had 2 images, I thought it always chose the biggest. That isn't true, it just never chose the image I expected ;)