Responsive Images: the solutions so far and a mixed new one

I read the other day this fine article by Mat Marquis about his experiences, searches and conclusion on the issue of responsive images. It sparked my interest to look at the subject a bit more thoroughly.

What are the responsive images? Or, better, what they should be? For a more elaborate explanation read Mat’s article. For me, it suffices to say this: an image is considered responsive when it adapts both to the size of a viewport as well as to the bandwidth of a device. Usually, these two go hand in hand: the smaller the device, the higher the probability that is connected to a slow network (like our mobile phones).

It’s this second requirement (bandwidth) that makes the issue of responsive images complex. Because there is no ubiquitously  accepted methodology or technology to measure the relative abundance or scarcity of this resource.

So, the rule here is simple to understand, yet difficult to implement: the less bandwidth we have at our disposal, the smaller the file size of an image should be.

This rule, though, is meaningless, if taken in isolation. A desktop computer with a huge screen and a sluggish network connection does NOT need small file size images, as this can impact adversely the quality of a web page rendered to its full extent. We should be talking about smaller file sizes in conjunction with the requirement for smaller image dimensions.

Enough said about theory. What are the proposed solutions to the problem?

I have traced four kinds of solutions:

  • CSS based
  • Script based
  • Server hacks
  • A combination of two or more of the above.

Surprisingly, there is no pure HTML based solution. And this is what Mat pinpoints in the aforementioned article, as well as what he considers the road ahead.

Here is an example that highlights  how html should be according to Mat:

<picture>
   <source src="high-res.jpg" media="min-width: 800px" />
   <source src="mobile.jpg" />
   <!-- Fallback content: -->
   <img src="mobile.jpg" />
</picture>

What do we have here? A proposal for HTML5 to treat the image tag much like the video or audio tag, along with the subordinate source tags and their  media attributes that allow us to load different images utilizing media queries.
It’s a very elegant solution with two drawbacks:

  • If we ever come to the point where bandwidth is not an issue, the solution will become irrelevant: the current img tag with the resizing it allows, is less verbose.
  • It’s a solution currently out of our control.

Let’s now take a closer look to the existing solutions.

CSS Based Solutions

There is no way to set the source attribute of an image through CSS so this approach relies on a trick: use a substitute for the img tag that can be set through CSS. The handy one is the background-image attribute for block elements. Media queries are used to determine which image to assign to this attribute. For example:

@media screen and (max-width: 480px) {
 div.someimage {
  background-image: url(small.jpg);
  background-size: auto;
 }
}
@media screen and (min-width: 480px) {
 div.someimage {
  background-image: url(big.jpg);
  background-size: auto;
 }
}

You would probably need some more rules here to make it work in a real case, but this is for demonstration purposes only. When the page loads, the media queries determine which part of the stylesheet is applicable, and this in turn, determines which image to fetch.
The problem with this solution is that it is not semantically correct and that it alters the behavior the user expects from the images (i.e. can’t right click and download). Let alone that it poses a burden to the web developer and the user that will create future content.

Script Solutions
The information about the two or more image files needed could resize inside an image tag with the use of data attributes : <img src="small.img" data-bigimage="big.jpg" />
Once the dom has finished loading, a small script can be put to work to a. determine is there is a need for a bigger image and b. if yes, substitute the value of the src attribute with the value of the data-bigimage attribute.
This solution is not optimal since a desktop computer will have to load two images (small.jpg first and big.jpg later) while only one is needed.
To save bandwidth and speed up things the image tag could come without a value :
<img src="small.img" data-smallimage="small.jpg" data-bigimage="big.jpg" />
With browser detection or media queries we determine which one of the data attributes fits our purpose and substitute the image source attribute with it. Then only the desired image is loaded.
But this solutions fails when the browser does not support scripting, or the user disables it, or when, for some reason, the script stops executing before reaching this point.

    $(document).ready(function(){
      if(window.screen.width > 480){
        $("img").each(function(){
          this.src = $(this).attr('dataset['bigimage']');
        });
      }
    });

Server based solutions
If the server has a means of knowing upfront what kind of device it will be servicing, it can determine also what kind of images to send. Device recognition is a shaky issue, mostly because it relies on information passed from the browser to the server, something that can be altered or forged. But, assuming we have it, then the method would work as follows:
Image tags source is set to a high resolution image.
If the server detects a small device, then a script kicks in to resize the image, serve it and cache it for future use.
The benefit of this approach is that it requires no changes to the HTML. If device recognition fails, then a big image will be sent to the device which might be difficult to load but the page won’t break. (For more info on this approach look at adaptive images).

Mixed Solutions

Javascript is the extra ingredient most often needed in conjunction with another approach. So, for instance, in the server based solutions mentioned above, one could determine the device dimensions through a cookie set by javascript on the page HEAD tag.

document.cookie='resolution='+Math.max(screen.width,screen.height)+'; path=/';

A new(?) approach

If I were to choose one of the above solutions, I would go for the CSS one. This is both a matter of personal preference as well as because media queries is a really handy and unobtrusive way to determine device.

So to mend the shortcomings of the solution presented above I would augment it with the help of javascript. I would let the browser determine the device through media queries and load the images as background images of div elements and then run a script to change these elements to proper images. To determine which containers’ background images should be ‘translated’ to proper images, I would use an distinct class (‘.responsive’ in the example below).

    $(document).ready(function(){
      $(".responsive").each(function(){
        var imgsrc = $(this).css('background-image');
        imgsrc = imgsrc.substr(4, -5+imgsrc.length);
        $(this).after("</pre>
<img src="&quot; + imgsrc + &quot;" alt="" />
<pre>");
        $(this).remove();
      });
    });

Conclusion
The above are not meant to serve as a tutorial of some sort, neither as a comprehensive survey of the solutions proposed. Writing a blog post has always been to me a way to put some order in my thoughts and clarify obscure issues through the valuable feedback a post attracts. And this is precisely what this post serves.
The responsive images problem is an open problem. The solution to pick should be the one that fits mostly to your type of application and the one the diminishes the shortcomings in each case.

About these ads

4 thoughts on “Responsive Images: the solutions so far and a mixed new one

  1. Why not run a script in the head of the page, which identifies the device and sends an AJAX request to the server, informing on the device type, so that the server can decide what kind of images to send while the rest of the page loads?

    1. As I say in the article, a scripting solution is not optimal because the scrip can fail or scripting may be disabled in a browser.
      Otherwise, what you suggest is more or less the approach I mentioned briefly in the mixed solutions. Have a look again.
      The ‘while the rest of the page loads’ part is tricky though: what if the page loads before the the completion of the ajax request? Such a solution should be implemented synchronously to prevent this. I am not sure how.

  2. Perhaps the server could make the decision, based on the browser agent which is info that we have in the very first request.
    Provided of course that the browser does not spoof it’s identity

Comments are closed.