Aliaspooryorik
ColdFusion ORM Book

Loading mobile friendly images

My collegue, Andy Beer, and I have been working on a mobile optimised version of a website. CSS media queries provide an excellent way of rendering an appropriate layout for the screen size, however there is one issue that they don't solve and that's images.

You may have a website that has nice big banner images which look awesome on a monitor at 960px wide, but on a mobile version of the same website those banner images are too big in terms of both width and download size. What we wanted to do was use the same HTML markup for all devices, but only load the appropriate imagery for the screen size.

There are a number of solutions to this problem, all of them have pros and cons, so let's dive right in!

CSS background images

One of the neat things about CSS media queries is that if you reference an image in your CSS with a media query block, the device will only download that image if the screen resolution is correct. Here's a quick example:


/* mystyles.css */
@media only screen and (max-width: 480px) {
#header { background-image:url('myheader-480px.png'); }
}
@media only screen and (min-width: 481px) and (max-width: 800px) {
#header { background-image:url('myheader-800px.png'); }
}
@media only screen and (min-width: 801px) {
#header { background-image:url('myheader-960px.png'); }
}

This is great as you're saving on bandwidth and page rendering is faster. The downside is that you have to reference all your images in the CSS, which is not really practical.

Loading with Javascript

Another solution is to use Javascript and HTML5's data-* feature.


<!DOCTYPE html>
<meta charset=utf-8>
<title>Some page</title>
<script type=text/javascript src=//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js></script>
<script type=text/javascript>
jQuery(function($){
// get the document width
var documentWidth = $(document).width();

// blank means use the image from the src attribute
var screenType = '';

if (documentWidth >
800) {
// big screen
screenType = 'big';
}
else if (documentWidth > 480) {
// medium device, for example a tablet
screenType = 'tablet';
}
if ( screenType != '' ) {
// find all images with the data-src-xyz attribute
$('img[data-src-' + screenType + ']').each(function(){
$this = $(this);
// set the src attribute to the optimised version
$this.attr('src',$this.attr('data-src-' + screenType));
});
}
});
</script>

<!-- Note: use the mobile optimised image as the default -->
<img src="logo-mobile.png"
data-src-tablet="logo-tablet.png"
data-src-big="logo-big.png">

OK, if you can get past the fact that I haven't closed my tags and there's no head or body tags (as HTML5 doesn't need them), then you'll see that I have an inline image with alt, src, data-src-mobile and data-src-tablet attributes (I thought I'd be nice and add the quotes!). At the top of the page is some simple jQuery to detect the screen size and load the appropriate image.

There are a couple of important things to note in this example. Firstly, the javascript can't be run until the DOM has loaded (as it needs to know if there are any img tags on the page), and more importantly, that I'm referencing the mobile friendly version in the src attribute and the bigger screen versions of the image in the data-src-mobile and data-src-mobile attributes. Why? Well, the browser will try to load the image that it finds in the src attribute, so this means that on the larger screens both the logo-mobile.png and the logo-big.png will be downloaded (try it in firebug if you want proof). As the mobile version is less likely to have bandwidth or a fast connection, that is the one I reference in the src attribute as it will only download the logo-mobile.png version as the jQuery code will not load the logo-big.png version as the screen size is too small.

So what happens if you don't have specific versions for each screen size?

The following line basically means that only images that have an attribute that matches the screen size will be replaced. So if you haven't got a nice high quality version, then the mobile version (specified in the src attribute) will be shown instead.


$('img[data-src-' + screenType + ']').each(function(){

Loading with Javascript variation

The Javascript version works well on mobile devices (or small screens), but the desktop user with the nice monitor ends up downloading two versions of the images, even though they will only see the big version. This isn't ideal and if they have Javascript disabled then they'll only see the small/low quality versions. There isn't (as far as I know) a perfect solution to this, but there is an imperfect solution!


<!DOCTYPE html>
<meta charset=utf-8>
<title>Some page</title>
<script type=text/javascript src=//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js></script>
<script type=text/javascript>
jQuery(function($){
// get the document width
var documentWidth = $(document).width();

// default to mobile
var screenType = 'mobile';

if (documentWidth >
800) {
// big screen
screenType = 'big';
}
else if (documentWidth > 480) {
// medium device, for example a tablet
screenType = 'tablet';
}
// find all images with the data-src-xyz attribute
$('img[data-src-' + screenType + ']').each(function(){
$this = $(this);
// set the src attribute to the optimised version
$this.attr('src',$this.attr('data-src-' + screenType));
});
});
</script>

<img src="about:blank"
data-src-mobile="logo-mobile.png"
data-src-tablet="logo-tablet.png"
data-src-big="logo-big.png">

All I've done here is changed the src attribute to "about:blank" and added a data-src-mobile which references the mobile friendly version of the image. By doing this only one version of the image will be downloaded, the downside is of course that you'll need to have Javascript enabled.

Possible Improvements

You could modify the jQuery to execute when the page is resized, you may also want to hide images using CSS (and enable with Javascript) so that users without Javascript won't see placeholders. Both of these don't really have many real-world situations where they would be a requirement, and hiding images may have issues if you have rich content that isn't allows visible.

So there you go, I'm by no means a mobile optimisation expert, but this is what I've found through trial and error.


4 comments

  1. John, in your last version could you not cover all bases by putting the default image in a NOSCRIPT block? Means more code, but it would ensure a single image is loaded in every scenario.

    Comment by Julian Halliwell – May 25, 2011
  2. Hi Julian, unfortunately that won't work as a browser with JavaScript disabled will try to display both images and the first one will be a broken image. To make it work you'd have to use JavaScript to write out the first image.

    Comment by John Whish – May 26, 2011
  3. John, you're right. Adding the IMG to the DOM with script is quite do-able, but means specifying it twice which is not ideal. I did wonder if you could clone the image already defined in noscript, but for obvious reasons you can't access noscript elements with JS.

    There must be a better solution which covers all bases...

    Comment by Julian Halliwell – May 26, 2011
  4. You should check out Sencha.IO

    Comment by jeff – July 29, 2011

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.

Please subscribe me to any further comments
 

Search

Wish List

Found something helpful & want to say ’thanks‘? Then visit my Amazon Wish List :)

Categories

Recent Posts