Adaptive images with responsibility


Extension of the <noscript> technique for responsive images. Includes the jQuery plugin for this.

[Note] There is an extension of this post – Responsive anything, please.
These days only the laziest doesn’t talk/read/have an opinion about the responsive, also known as “adaptive”, design. Moreover the number of the articles on the adaptive images is growing like the mushrooms after rain. I’m not an exception, and would like to share my notes about managing flexible images the way I do it and like… to some extent.

Theoretical part.

The theory of adaptive images is described elsewhere, including almost every second post in this year’s 24 ways advent calendar so I am not going to dive into details and just cover the most basics. Adaptive a.k.a. “responsive” a.k.a “flexible” images, are one of the corner-stones of the responsive design puzzle (together with fluid grids and CSS3 media queries). But while the two rest techniques are more or less well-defined, there is no single universal technique when it comes to the adaptive images. The flexibility of the images itself is not a rocket science, you just need a tiny bit of CSS:

 img { max-width: 100%; } 

Maybe you will need some more CSS and javascript in addition if you plan to support IE6 and IE7, but it’s beyond the scope of this post. I recommend you read "Responsive Web Design" book by Ethan Marcotte or, at least, check out this A List Apart article with a portion of Chapter 3 of that book.

Responsible part.

The ease of images’ scaling makes it too easy to build web pages with full-size high-res images that scale down. But it’s not a “yay!” case, more like “meh”. That means even mobile visitors of your site will have to download huge files on their EDGE/3G connection. Due to this issue, another term popped up — responsible web design that, under the hood, just means “think what you’re doing”. Also, not every image can be easily scaled down while preserving its meaning.

The solutions for dealing with the aforementioned issues are mainly split into two categories: server-side and client-side. Jason Grigsby at Cloud Four compiled a very good reference of the most popular techniques available.

When it comes to the server-side solutions for the responsive images, it assumes some constraints like server-side technology, server set up, a certain folders structure on the server, etc. This significantly limits the usage of such techniques. Also, there are 2 important things. First, I am front end developer and like things that don’t require me setting up a server. Second, the idea of the responsive design in it’s origin belongs to the front end. Due to these 2 reasons, I wanted to have a way of managing flexible images on the client side.

About a month ago, .net magazine published an interview with Mairead Buchan, front end developer at Head. She was talking about how Head is using adaptive images using <noscript> tag. That was brilliant and simple. She also wrote a blog post about the original technique that I recommend you to check out. Quite a few assumptions I make further in this article are based on that work.

But there were some things that I was not happy with:

  • Additional markup element without any real semantical meaning. Why one might not like extra markup element is clear, I assume.
  • No jQuery solution. I know, this point is arguable. Not using jQuery for this purpose is actually an advantage since it means faster fetching of the images, less noticeable reflow of the content. On the other hand, jQuery helps me build interactive projects easier and almost every project I work with has it.
  • Lastly, and it’s true for almost all of the solutions out there — no selectivity. That means, it’s either or — either you update all of your images at once, marked as responsive, or you don’t. The problem with this is there are different images and at a certain browser width, I might want to manage different images differently. Example — portrait and landscape images — portraits depend on the window’s width less than the landscape ones.

DIY part.

I needed a way of managing images in my projects so decided to write code that suits my needs better than the existing solutions. Here is the markup, influenced by original <noscript> technique, but without extra <span> element:

<noscript class="adaptive" data-img-alt="Fancy banner."
    <img src="small_banner.jpg" alt="Fancy banner." />

There are some important things to keep an eye on

  • we use <img/> that is not just a placeholder, but delivers meaningful image (the smallest size) when javascript is disabled. We link it to the smallest version of the image because, probably, if a device has JS disabled it has more chances to be an older mobile phone than a desktop.
  • data- atributes on the <noscript> tag. With those you define an alternative text for an adaptive image and paths to the files with different sizes of the image (’s’ for small, ‘l’ for large and ‘m’ for medium in this case).

Next we have some javascript (jQuery, to be presize):

var initWidth = document.documentElement.clientWidth;
 if (initWidth <= 480) {
     // The smallest images for the screens < 480px
 } else if ((initWidth > 480) && (initWidth <= 1024)) {
     // Medium images for the screens between 480 and 1024px
 } else {
     // Large images for the screens > 1024px

Take a note that we use document.documentElement.clientWidth and not screen.width as most of the techniques do. Let’s check the difference between the two:

  • screen.width gives the width of the device’s screen. The problem — iOS, Palm WebKit, Opera Mobile on Windows Mobile always give portrait size no matter what. This leads to downloading, possibly smaller version of an image when in the landscape mode that can lead to the design breakage on these devices.
  • document.documentElement.clientWidth is the JS analogue of the CSS3 media queries. As far as I understand, wherever CSS3 media queries work, this will work as well. In other cases, all our adaptive technologies supposedly fail any way.

Back to the script. As you can see, we select all elements with CSS class adaptive and call method adaptImages() on those with passing a desired size of the image. This should give us small images on the smallest screens, medium-sized images on the screens between 480 and 1024 pixels and large images on the larger screens.

Naturally, as the next step, I wrote adaptImages, a jQuery plug-in:

$.fn.adaptImages = function (size) {
    return this.replaceWith(function () {
        var $this = $(this),
            newImg = new Image();

        $(newImg).attr('alt', $this.attr('data-img-alt'))
                 .attr('src', $this.attr('data-img-' + size));
        return newImg;

Now, if we add up both javascript snippets we can write down the actions we perform:

  1. Select all elements with CSS class adaptive and call adaptImages() on those.
  2. Generate the image node for the requested size. As a parameter, adaptImages() accepts the string, defining the size of the image needed. This should correspond to data-img-SIZE attributes of the adaptive elements.
  3. Replace the processed adaptive element with the generated image. What’s the point of keeping it if we don’t update images after they have been adapted once?

So, where is the selectivity?

As one of my concerns with the original <noscript>, I have mentioned lack of selectivity. Let me show what I meant and how I achieve this with the code above.

Let’s say I have 2 types of images on the page — 1 large banner, spanning the whole site’s width and some smaller images in the article’s content. Something like this:

<section class="banner">
    <noscript class="adaptive" data-img-alt="Fancy banner."
        <img src="small_banner.jpg" alt="Fancy banner." />
<section class="content">
    <h1>An Article</h1>
    <p>Some content</p>
    <noscript class="adaptive" data-img-alt="Just a within-content image."
        <img src="small_content_img.jpg" alt="Just a within-content image." />
    <p>Some more content</p>

For the smallest screens I would like to not load the content images at all to leave more space for the text itelf. Banner should be loaded, but in the smallest possible version. It’s not harder than adding the following strings to our javascript:

… if (initWidth <= 480) {
    // The smallest images for the screens < 480px
    $('.banner .adaptive').adaptImages('s');
} …

Wait a second, this just tells to render smallest images within .banner. What about the content images? Since they are placed within <noscript> they will simply be ignored by browsers and will not be fetched at all. Ideally, if you want to be consistent and not show an image even if JS is disabled, for such images might need to update the markup to have something like <span class="adaptive"> instead of <noscript class="adaptive"> with the same attributes. This will also require the function’s update, but we will not review that in the current post.

Another case when you might want to manage images separately on the page — images containing text like infographics, charts and so on. You will, most probably, need to provide images with different content for different sizes in order to preserve the readability of the text in such images. And those images will almost for sure have different breaking points — image will need to be updated to the higher-resolution version at 460, 640, 768 and 1024 instead of just 480 and 1024.

The demo page.

Make sure you check the demo page on different screen widths and check the images’ src attributes to make sure they are different on different widths. Also, you are more than welcome to check the source of that page to see how the things are glued together.

Clearly, adaptive images are more of a hand-crafted work than a mass-production either-or solution.

blog comments powered by Disqus