Is there something wrong with my srcset definition, or is current browser support just weak?

slamci picture slamci · Feb 23, 2015 · Viewed 11.2k times · Source

I'm trying to get my head around the new 'srcset' attribute of 'img'. I've built a simple test page, but the behavior I see from browsers is surprising.

First, here's the test page:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Test Page</title>
    <style type="text/css">
section.wrapper {
    width: 100%;
    text-align: center;
}

section.wrapper div {
    margin: 0 auto;
}

section.wrapper div img {
      width: 288px;
      height: 216px; 
}

@media (min-width: 30em) { }

@media (min-width: 40em) {
    section.wrapper div img {
          width: 576px;
          height: 432px; 
    }
}

@media (min-width: 48em) { }

@media (min-width: 64em) {
    section.wrapper div img {
      width: 720px;
      height: 540px; 
    }
}

@media (min-width: 72em) {
    section.wrapper div img {
      width: 960px;
      height: 720px; 
    }
}   

    </style>
</head>
<body>

<section class="wrapper">
<div class="imagecontainer">
<img src="images/image-720.jpg"
     srcset="images/image-1920.jpg 1920w,
             images/image-1440.jpg 1440w,
             images/image-1152.jpg 1152w,
             images/image-960.jpg 960w,
             images/image-840.jpg 840w,
             images/image-720.jpg 720w,
             images/image-576.jpg 576w,
             images/image-420.jpg 420w,
             images/image-288.jpg 288w,
             images/image-144.jpg 144w"
     sizes="(min-width: 40em) 576px, (min-width: 64em) 720px, (min-width: 72em) 960px, 100vw"
     alt="Test" />
</div>
</section>

</body>
</html>

I'm testing on a Retina Macbook Pro, and a Retina iPod, with Chrome/40.0.2214.115 and Firefox 35.0 (on the Macbook Pro), and Chrome/40.0.2214.73 and Safari/600.1.4 (on the iPod). I have turned on srcset support in Firefox. I'm testing on a fast connection.

What I'm trying to see is whether the various browsers can intelligently get an appropriately-sized image. However, they're not behaving the way that I'd expect.

For the iOS browsers, my expectation is that they'd use the fallback value of '100vw' from the 'sizes' attribute. As the viewport width for an iPod in portrait mode is 320px, I'd expect them to go for either the 288px or the 420px image. Or, possibly, given that it's a Retina device, they'd request the 576px or 840px images. Instead, both Safari and Chrome request the 1920px image.

The desktop Firefox browser makes two requests. The first is for the default image specified in the 'src' attribute. The second is for the 1920px image. It always requests the largest image, irrespective of the current window size.

Desktop Chrome comes closest to actually exhibiting what I'd think of as the correct behavior, although even that's quirky. If my test file is local, it always grabs the 1920px image, irrespective of viewport size. If I put the test file on a remote server, it exhibits a general preference for the 1152px image (even when the image should be rendered at 960px, making 1920px the logical choice for a 2x device). If I clear Chrome's cache, and resize the window, then reload, it will sometimes request smaller images.

If what I'm seeing is the expected behavior for these browsers, given the current state of each browser's support, then it's clear to me that I probably shouldn't provide the higher-resolution images -- better to serve an image that looks less perfect on a 2x display than to dump a giant JPEG on a mobile device over a possibly-slow connection. And if Firefox is always going to pull the default image, then I should probably make that smaller rather than larger.

Is there a problem with my code that is causing these behaviors? And if there isn't, are there any current best practices for using srcset that cope with the idiosyncrasies of the various current implementations, taking advantage of the possibilities of the feature without causing over-large images to be downloaded?

Answer

alexander farkas picture alexander farkas · Feb 23, 2015

In general with srcset you can't predict the chosen candidate.

Desktop Chrome: Chrome actually runs some very good but unpredictable algorithms to get the right image candidate. For example, if it is cached or as you said local it will use the largest image. Here everything should be fine.

Desktop Firefox: You seem to check with srcset on, but picture off. srcset with width descriptor and sizes attribute can only be enabled if both srcset and picture flag is enabled. But I would suggest that you test with current FF nightly because this feature will be truned on with 38 and to do so some bugs were fixed. Otherwise you will get the same behavior, you get with Safari for iOS. FF38 will have the desired behavior.

iOS: Chrome on iOS is not Chrome. The iOS browser does not support srcset with the width descriptor, but only supports the x descriptor. Even worse, if you use the width descriptor iOS thinks all candidates are equal (1x) and will load the first candidate from the srcset list.

Here is how you can deal with all your problems:

  1. Use a good polyfill: respimage or picturefill do the job
  2. Even if you don't use art direction use the following pattern:

<picture> <source srcset="images/image-1920.jpg 1920w, images/image-1440.jpg 1440w, images/image-1152.jpg 1152w, images/image-960.jpg 960w, images/image-840.jpg 840w, images/image-720.jpg 720w, images/image-576.jpg 576w, images/image-420.jpg 420w, images/image-288.jpg 288w, images/image-144.jpg 144w" sizes="(min-width: 40em) 576px, (min-width: 64em) 720px, (min-width: 72em) 960px, 100vw" /> <img /> </picture>

If you want to have more control you can also use lazySizes which gives you an autosizing calculation out of the box and in case you want to constrain the pixel ratio for mobile browsers (1.6x instead of 2x for example), the optimumx plugin.

You then could write your markup like this:

<picture> <source data-srcset="images/image-1920.jpg 1920w, images/image-1440.jpg 1440w, images/image-1152.jpg 1152w, images/image-960.jpg 960w, images/image-840.jpg 840w, images/image-720.jpg 720w, images/image-576.jpg 576w, images/image-420.jpg 420w, images/image-288.jpg 288w, images/image-144.jpg 144w" /> <img class="lazyload" data-sizes="auto" data-optimumx="1.6" /> </picture>