Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

Quick Tip: How to Use HTML5 “picture” for Responsive Images

by
This post is part of a series called Strange and Unusual HTML Tags.
Quick Tip: Don't Forget the Viewport Meta Tag

Images are notoriously one of the most challenging aspects of responsive web design. Today we'll look at how the <picture> element, a solution to the problem of responsive images, can be used right now.

First, the Problem

The days of fixed-width, pixel perfect website design are well and truly behind us. In the present day of widescreen monitors, internet TVs, multiple sized tablets and smart phones our designs now have to cater for everything from 320px wide up to potentially as high as 7680px wide.

Along with this multi-resolution landscape comes a need for images to stretch or shrink to fit these wildly varying requirements. This can prove to be something of a problem given that, with the exception of vector graphics, the vast majority of images have specific pixel based widths that do not change.

So what do we do?

The Current, Most Common Solution

As a general rule, you'll find the following in just about any responsive site's CSS:

img {
    max-width: 100%;
    height: auto;
}

This code uses the max-width: 100%; setting to ensure images never go beyond the width of their parent container. If the parent container shrinks below the width of the image, the image will scale down along with it. The height: auto; setting ensures the images' aspect ratio is preserved as this occurs.

One fluid image for all circumstances

It solves the problem in one respect, allowing us to display the same image under many different circumstances. But it doesn't allow us to specify different images for differing circumstances.

A New Solution: <picture>

<picture> is a new element which is set to become part of HTML5. 

It will bring the process for placing responsive images up to speed with the way the current <audio> and <video> elements work. It will allow you to place multiple source tags, each specifying different image filenames along with the conditions under which they should be loaded.

It will allow you to load an entirely different image depending on:

  • Media query results e.g. viewport height, width, orientation
  • Pixel density

This in turn means you can:

  • Load appropriately file sized images, making the best use of available bandwidth.
  • Load differently cropped images with different aspect ratios to suit layout changes at different widths.
  • Load higher resolution images for higher pixel density displays.
Different images served, depending on the circumstances

How Does <picture> Work?

The basic steps of working with <picture> are:

  1. Create opening and closing <picture></picture> tags.
  2. Within those tags, create a <source> element for each query you want to run.
  3. Add a media attribute containing your query on things like viewport height, width, orientation etc.
  4. Add a srcset attribute with the corresponding image filename to load.
  5. Add extra filenames to your srcset attribute if you want to provide for different pixel densities, e.g. Retina displays.
  6. Add a fallback <img> element.

Here's a basic example which checks if the viewport is smaller than 768px, then if so loads a smaller image:

<picture>
    <source srcset="smaller.jpg" media="(max-width: 768px)">
    <source srcset="default.jpg">
    <img srcset="default.jpg" alt="My default image">
</picture>

You'll notice that the syntax used in the media attribute is the same as you might be used to from creating CSS media queries. You can use the same checks, meaning you can query max-width, min-width, max-height, min-height, orientation and so on.

You can use these checks to do things like loading landscape or portrait versions of an image depending on device orientation, and you can still mix in size queries at the same time. For example:

<picture>
    <source srcset="smaller_landscape.jpg" media="(max-width: 40em) and (orientation: landscape)">
    <source srcset="smaller_portrait.jpg" media="(max-width: 40em) and (orientation: portrait)">
    <source srcset="default_landscape.jpg" media="(min-width: 40em) and (orientation: landscape)">
    <source srcset="default_portrait.jpg" media="(min-width: 40em) and (orientation: portrait)">
    <img srcset="default_landscape.jpg" alt="My default image">
</picture>

The above code loads a smaller, landscape cropped version of the image on a smaller, landscape oriented device. It loads a larger version of the same image on a larger landscape oriented device. 

If the device is portrait oriented it loads a portrait cropped version, at small size on a small device or at large size on a large device.

If you want to provide different resolution versions of your images for higher density displays, you do so by adding extra filenames to the srcset attribute. For example, let's look at our first snippet of code from above with handling for Retina's 2x resolution added:

<picture>
    <source srcset="smaller.jpg, smaller_retina.jpg 2x" media="(max-width: 768px)">
    <source srcset="default.jpg, default_retina.jpg 2x">
    <img srcset="default.jpg, default_retina.jpg 2x" alt="My default image">
</picture>

The media query is still evaluated first so you can control the dimensions your image will appear at on screen. Then the display's pixel density will be checked and if higher densities are both supported and allowed by the user's preferences, the higher density version of image will be loaded.

Using <picture> Today

Right now native implementation for <picture> is in the works for Chrome, Firefox and Opera. In the future it's likely we'll see widespread support as other browsers also catch on. But for the moment that support is still yet to arrive.

In the meantime, you don't have to wait if you'd like to start using <picture> right now. You simply have to use Picturefill 2.0; a polyfill provided by those clever folks at Filament Group.

After downloading the picturefill.js file to your project it can be implemented by simply loading it in your site's head section:

<script src="picturefill.js"></script>

There is also an option to load the script asynchronously for added efficiency, which you can read about in Picturefill's documentation.

With this script loaded, the <picture> element will work as I've explained, with only a few limitations.

Picturefill Limitations

IE9

Picturefill works just fine with other IE versions, however IE9 doesn't recognise source elements that are wrapped in picture tags. To get around this, conditionally wrap your source elements in video tags which will then make them visible to IE9, for example:

<picture>
    <!--[if IE 9]><video style="display: none;"><![endif]-->
    <source srcset="smaller.jpg" media="(max-width: 768px)">
    <source srcset="default.jpg">
    <!--[if IE 9]></video><![endif]-->
    <img srcset="default.jpg" alt="My default image">
</picture>

Android 2.3

Like IE9, Android 2.3 can't see source elements inside a picture element. However, it can understand the srcset attribute when used on a regular img tag. Be sure to always include your fallback img element with the default filename in the srcset attribute for Android 2.3 and any other browsers that may have the same issue.

Requires JavaScript and Native Media Query Support

With this being a JavaScript based solution, it accordingly requires JavaScript to be enabled in the browser. Picturefill 2.0 doesn't provide a "no-js" workaround because if it did, multiple images would appear when native browser support for <picture> is rolled out. However, you do have the option to use Picturefill 1.2 if a "no-js" option is a must have for you.

The other requirement Picturefill has is for native media query support, to enable the queries in the media attribute to work. All modern browsers support media queries, with IE8 and lower being the only non-supporting browser with a small remaining user base.

Possible Extra HTTP Requests

In browsers which have native support for srcset, but not yet for picture, it's possible the filename specified in the fallback img element might be requested before a better fitting image from the source elements is determined.

This is only a temporary issue and will go away once native picture implementation is rolled out.

More Information

Try out <picture> in your project today and see what you think!

Advertisement