Responsive images: picturefill type attribute

I’m working on a new responsive site and decided to finally delve into the responsive image quagmire. I’ve been following the goings-on for awhile, but not in any great detail. After doing some research, I wanted to focus on two approaches: first, using inline SVGs whenever possible, so the images will scale and look great at any size; second, when SVGs aren’t an option, conditionally loading different size images based on media queries.

Scott Jehl’s picturefill script seemed like a good starting point; picturefill is a JavaScript polyfill for the proposed new <picture> element. <picture> is interesting because, like the existing <video> tag, it uses <source> tags for media. Unlike <video>, though, the current <picture> proposal doesn’t recognize type attributes on <source> tags.

1
2
3
4
5
6
7
8
9
10
11
<div data-picture data-alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia">
  <div data-src="small.jpg"></div>
  <div data-src="medium.jpg"     data-media="(min-width: 400px)"></div>
  <div data-src="large.jpg"      data-media="(min-width: 800px)"></div>
  <div data-src="extralarge.jpg" data-media="(min-width: 1000px)"></div>

  <!-- Fallback content for non-JS browsers. Same img src as the initial, unqualified source element. -->
  <noscript>
    <img src="external/imgs/small.jpg" alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia">
  </noscript>
</div>
Figure 1: HTML for picturefill.

With <video>, the browser will load the first <source> with a compatible type. A type attribute on <picture> would allow me to load an SVG if it was supported, and fall back on JPEGs with media queries if it wasn’t, as in Figure 2. This has been discussed at the Responsive Images Community Group in a blog post by Brett Jankord.

1
2
3
4
5
6
7
8
9
10
11
12
13
<div data-picture data-alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia">
  <!-- If browser supports inline SVG, use image below: -->
  <div data-src="external/imgs/vector.svg" data-type="image/svg+xml"></div>

  <!-- Otherwise, fallback on JPEGs -->
  <div data-src="external/imgs/small.jpg"></div>
  <div data-src="external/imgs/medium.jpg" data-media="(min-width: 400px)"></div>
  <div data-src="external/imgs/large.jpg" data-media="(min-width: 800px)"></div>
  <div data-src="external/imgs/extralarge.jpg" data-media="(min-width: 1000px)"></div>

  <!-- Fallback content for non-JS browsers. Same img src as the initial, unqualified source element. -->
  <noscript><img src="external/imgs/small.jpg" alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia"></noscript>
</div>
Figure 2: Using a type attribute to load an SVG if it’s supported

With this strategy in mind, it was relatively simple to modify picturefill to accomodate a type attribute. I included a function to detect whether support for SVG was available, with the actual detection based on Modernizr’s inlinesvg test. (I also threw in a test for WebP, again based on Modernizr.) Now when looping through the source elements, the script will match on media with no type, image/svg+xml if supported, and image/webp if supported. (Additional types for JPEG, PNG, and GIF could easily be added, but I chose to just leave out the type attribute for graphics with [near-] universal support.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div data-picture data-alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia">
  <!-- If browser supports inline SVG, use image below: -->
  <div data-src="external/imgs/vector.svg" data-type="image/svg+xml"></div>

  <!-- Otherwise, if browser supports WebP, use images below: -->
  <div data-src="external/imgs/small.webp" data-type="image/webp"></div>
  <div data-src="external/imgs/medium.webp" data-media="(min-width: 400px)" data-type="image/webp"></div>
  <div data-src="external/imgs/large.webp" data-media="(min-width: 800px)" data-type="image/webp"></div>
  <div data-src="external/imgs/extralarge.webp" data-media="(min-width: 1000px)" data-type="image/webp"></div>

  <!-- Otherwise, fallback on JPEGs -->
  <div data-src="external/imgs/small.jpg"></div>
  <div data-src="external/imgs/medium.jpg" data-media="(min-width: 400px)"></div>
  <div data-src="external/imgs/large.jpg" data-media="(min-width: 800px)"></div>
  <div data-src="external/imgs/extralarge.jpg" data-media="(min-width: 1000px)"></div>

  <!-- Fallback content for non-JS browsers. Same img src as the initial, unqualified source element. -->
  <noscript><img src="external/imgs/small.jpg" alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia"></noscript>
</div>
Figure 3: Using a type attribute to load an SVG or WebPs if they’re supported

In the example in Figure 3, we can expect the following results:

  1. If SVG is supported: the SVG will be loaded and all other sources will be ignored.
  2. If SVG is not supported but WebP is: the SVG will be ignored, the WebP images will be loaded based on their media queries, and the JPEGs will be ignored.
  3. If neither SVG or WebP are supported: the JPEGs will be loaded based on their media queries.
  4. If media queries aren’t supported or if JavaScript is turned off, the basic <img> tag will be used.

I’ve created a new fork of picturefill with these changes. Feel free to use this, and let me know what you think in the comments.

Updates

  1. Update @ : I just merged in Mat Marquis’s fork with Florian’s Compromise, and also split the files into a <div> version and a <picture>/<source> version. Updated at GitHub.
  2. Update @ Since I’m working on this for a WordPress blog, I’ve also now updated the GitHub repo to include a picturefill WordPress plugin.