Picturefill Needs to Die

Yesterday Scott Jehl posted a polyfill to support his idea of having a <picture> element with unlimited amounts of <source> elements. It's just honestly a bad idea and I know people agree by talking to them on Twitter and in person, but none have commented who disagree on his posts. First, let's look at his code snippet:

<picture alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia">
  <!-- smallest size first - no @media qualifier -->
  <source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_m.jpg">
  <!-- medium size - send to viewport widths 400px wide and up -->
  <source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5.jpg" media="(min-width: 400px)">
  <!-- large size - send to viewport widths 800px wide and up -->
  <source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_z.jpg" media="(min-width: 800px)">
  <!-- extra large size - send to viewport widths 1000px wide and up -->
  <source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_b.jpg" media="(min-width: 1000px)">
  <!-- extra large size - send to viewport widths 1300px wide and up -->
  <source src="http://farm8.staticflickr.com/7144/6547286841_c6160b34e2_o.jpg" media="(min-width: 1200px)">
  <!-- Fallback content for non-JS or non-media-query-supporting browsers. Same img src as the initial, unqualified source element. -->
  <noscript><img src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_m.jpg" alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia"></noscript>
</picture>

What is this scary looking markup you say? It's one image. Let's repeat that, one, single image.

The problem he's trying to solve isn't the same as <video> or <audio> tags as he tries to make it sound. He's trying to solve the problem that on small devices you don't want to load a large image which is a real issue. The reason why <video> and <audio> have terrible semantics where you need <source> elements is because browser vendors couldn't get get their shit together and just agree on a format. So, as a hack, they implemented these <source> tags so that browsers will skip tags one by one and keep going down the DOM until it knows how to render the format of one of them.

Those tags should have been implemented such as <img> tags were like <video width="500" src="myvideo.mp4">, but as I said, browser vendors couldn't agree. The issue Scott is trying to fix is completely different, yet tries to solve it the same way as these other tags tried to solve a different problem.

A Better Solution

First step in finding a solution to this is finding the real problem. The problem really isn't the screen resolution when you get down to it. If it was purely screen resolution we could simply load the highest resolution image we had and use CSS to set the width. We don't do this because of connection speed, not because of screen resolution. If you're on a phone and on 3G or a computer with DSL, or god forbid dial up, who gives a shit what your resolution is, I just want to see the fuckin' content. Don't make me wait for your stupid high-res picture just because my tablet has a 1280 screen but is downloading at <1MB down.

Let's try to solve this for real. We'd need a JavaScript API to return the connection speed. We then could decide what type of image to display. Here's what this could look like (jQuery is used just to keep the example clean):

//If the connection speed in >5MB
if(window.connection.speed > 5120 && screen.pixelRatio >= 2){
  //show high res
}
else if(window.connection.speed <= 5120 && screen.pixelRatio >= 2){
  //If we're on a slow connection, but you support high res, wait until high-res is done loading, then replace it with the low res one.
  $(high_res_version).appendTo('#hidden_loader_area').load(function(){
    $(low_res_one_on_page).replaceWith(high_res_version);
  });
}
else{
  //show low res
}

This is easy enough, but we don't want to have to require you to use JavaScript to show a high-res image, but we also don't want to force you to have half a dozen <source> tags to the exact same image with a different src. We could do this by using the media attribute which Scott suggested, but add a then and ({attr}={value}) syntax. Here'd be an example of this:

<img src="low-res.png" media="only screen and (min-device-pixel-ratio: 2) then (src=high-res.png)"/>

This is saying almost nearly the same thing as above, but with pure HTML. You have less control, but it's still semantic and still would work for most cases. Here we load the lower resolution image via the normal src attribute. Then, the media attribute would (in the background) check that the device is a screen with a pixel-ratio of 2 and if so then swap the src with high-res.png when high-res.png was fully loaded. The end user would just see that the image got “better”.

Conclusion

For god sakes do not send this to the W3C and if it makes it that far, don't make it a standard. It's bad for everyone. No one wants to hand code that and we don't need even more complexity to the DOM and more DOM to redraw as JavaScript programmers. Let's keep presentation separate from the structure. Image resolutions are purely presentational so let's keep it out of our DOM and let JS, CSS, or media type attributes handle the dynamics of screen resolution and connection speeds.