InternotesSharing Web Development Techniques

Blossoms

Responsive JavaScript

Posted on Thursday, 6th June 2013 by admin

Update: Modern browsers now implement a new picture element which will allow you to specify alternative images. This will be discussed elsewhere. Much has been said about the merits of Responsive Websites, and, for the most part, rightly so. The benefits of building a website which can cope with the plethora of alternative browsing devices must surely be obvious to any developer.

Another article will discuss the use of CSS in developing responsive web sites. This article focuses on the use of JavaScript to make your web site more responsive.

Things to Consider

When we move away from the assumption that your web site is being accessed by a (modern) desktop browser, we need to develop with the following in mind:

  • Mobile devices, especially phones, have smaller screens.
  • Mobile devices can typically change orientation.
  • Mobile devices often have lower bandwidth than is available on a typical desktop machine.
  • Mobile users are often searching for something, and expect a more appropriate experience.
  • Mobile devices don’t normally have attached keyboards and mouses, and so need an adapted screen space and interface.

To make things more complicated, you can’t simply assume all of the above. For example:

  • Many users use their mobile devices at home, where they have higher bandwidth through their wifi.
  • Many users use their mobile devices recreationally, and want more of a browsing experience.
  • Some laptop computers use a tethered connection through a mobile phone.

To fully cater for a broad range of users the following information would be invaluable:

  • What are the screen capabilities, especially its size?
  • What devices, such as mouse and keyboard are available?
  • What bandwidth is available?
  • What, in, fact, is the user trying to do?

To get the answer to the first one, CSS already has in place a mechanism called Media Queries. We can also use JavaScript to make things more dynamic.

The answers to the second and third will, in principal be available through JavaScript. However, This is not well supported and so somewhat unreliable. In the meantime we can use a few JavaScript tricks to at least estimate the bandwidth.

The answer to the fourth one is probably guesswork, especially since moods and situations change. It might, however, warrant some re-design of the site and using JavaScript to guess a good starting point.

Responding to Different Environments

The standard answer to the above questions is to learn more about CSS, and, in particular, how to make it more responsive.

Responsive CSS is largely a matter of including an appropriate media query, but CSS can only do so much. This article discusses how you can use JavaScript to further accommodate the characteristics, but first, let’s look at what CSS can do.

CSS Media Queries

For CSS, the magic is in the Media Query. A media query is an extension of the media type; where as the media type determines the type of device being used (such as screen or printer), a media query goes further to determine the characteristics of the device, such as its dimensions and orientation.

Just as with media types, a media query can be included as an @ command in a css file,

@media only screen and (min-width: 600px) {
    …
}

or in the link to an external css file.

<link rel="stylesheet" media="only screen and (min-width: 400px)" type="text/css">

The latter method has the additional advantage of not loading the CSS unless the media query is satisfied. This will be further discussed below. Mobile Devices

The main concern, of course, is coping with mobile devices, especially with mobile phones. This wonderful advance in technology has, in fact, plunged us back into the the dark ages of web design:

  • We have to cope with small screens, as we did when VGA was considered pretty good. In fact a standard mobile phone is only half of the VGA size, admittedly with better colour capabilities.
  • We have to cope with relatively slow and expensive band width, as we did when we were all using modems.

There is one redeeming factor: at least current mobile devices all have pretty up-to-date browser technology — worrying about IE6 is definitely not an issue. And all current mobile browsers will cope with what follows here.

The Issues

The two main issues with mobile devices are, of course fitting on a small screen, and reducing the data which is to be downloaded. Ironically, many solutions increase the downloaded data to allow for alternatives.

This needs to be stressed: dealing with mobile devices is not just a question of changing your layout. There are times when you need to actually change the content, either because mobile users may have different needs to desktop users, or simply to reduce the download burden, especially in the case of media content such as images.

While CSS Media Queries can certainly be used to reduce the CSS burden, that’s certainly not enough: CSS content is relatively small compared to other content, and CSS can’t influence what other content is downloaded.

That’s where JavaScript comes in.

Modern JavaScript1 actually implements Media Queries using a new function called matchMedia. It’s built into the window object, so, more formally, it’s called window.matchMedia.

Here is an example of how it is used:

var DesktopMQ = 'only screen and (min-width: 600px)';   //  same syntax as normal media query
var AnotherMQ = '…';

if(window.matchMedia(DesktopMQ).matches) {
    //  do something extra
}

if(window.matchMedia(AnotherMQ).matches) {
    //  do something extra
}

You will often see the use of a variable acting as a proxy for the function:

var mq = window.matchMedia; //  copy function for convenience

var DesktopMQ = 'only screen and (min-width: 600px)';   //  same syntax as normal media query
var AnotherMQ = '…';

if(mq(DesktopMQ).matches) {
    //  do something extra
}

// etc

Notice that in the example, the window.matchMedia function is copied into the variable mq. This is not only for convenience, but might also be incorporated into a feature test, in case you find a modern browser which doesn’t support it:

var mq = window.matchMedia; //  copy function for convenience
var DesktopMQ = '…';
if(mq && mq(DesktopMQ).matches) {   //  Check first whether we have a usable function
    …
}

Changing Content

So, what can you do once the media query matches? Given that CSS will already handle the presentation of your content, you are more likely to look at changing the content itself. This may include dynamically changing elements on your page.

Here are some examples:

  • Loading suitably sized images
  • Loading alternative navigation blocks
  • Loading optional content such as promotional material

Changing Images

A common suggestion is to use CSS background images instead of real images, and to use media queries to choose which ones. This misses the point of images, and still doesn’t solve the problem. Background images should be background images, and not be confused with content images which are there for a purpose. The test is how will your content be affected if CSS is turned off: do the missing images matter?.

One thing you might consider is using JavaScript to change the images which are loaded. There are two things to consider:

  • A smaller screen generally wants smaller images. Downloading a larger image and squeezing it into a smaller space doesn’t help your bandwidth, and may result in a badly scaled image.
  • Some mobile devices have a double resolution screen (such as the Retina display), which would benefit from a higher resolution image. This is also true of some desktop devices.

Working on the assumption that desktop devices have more and cheaper bandwidth (not always true if you’re on the road, trying to connect wirelessly!), we will design the page to use the smaller images for mobile devices, and then replace them with the larger images if the media query so indicates.

Version 1: Special Image Folders

If you can organise your images into folders for mobile and desktop versions, you might try something like this:

<img alt="…" src="images/mobile/something.jpg">

and then use the following JavaScript:

…
if(mq && mq(DesktopMQ).matches) {   //  Check first whether we have a usable function
    var images=document.getElementsByTagName('img');
    for(var i=0;i<images.length;i++) images[i].src=images[i].src.replace(/\/mobile\//,'/desktop/');
}

The script above finds all of the images, and replaces /mobile/ with /desktop/ in the image src. The extra slashes in the first parameter of the replace function are because it’s a Regular Expression, a sort of very clever matching code.

Version 2: Arbitrary Replacements

If you can’t or don’t wish to organise your images as above, you can instead include the alternative image in a special attribute of the image.

HTML5 defines a type of custom attribute, beginning with data-. JavaScript also includes new functions to extract this attribute, though, in this example, we will be using a more traditional approach.

We will define one or more alternative src attributes for different platforms.

<img alt="…" src="images/small.jpg" data-desktop-src="images/big.jpg" data-mobile-src="images/small.jpg">

The data-mobile-src attribute is not really necessary here, since it is already the default image src value, but may be useful if the screen is likely to be resized.

…
if(mq && mq(DesktopMQ).matches) {   //  Check first whether we have a usable function
    var images=document.getElementsByTagName('img');
    for(var i=0;i<images.length;i++)
    if(images[i]['data-desktop-src'])
    images[i].src = images[i]['data-desktop-src'];
}

This will take any image with the data-desktop-src attribute and use it to replace the src.

Resizing the Window

It’s unlikely that a mobile device will suddenly get bigger. In fact, since most mobile browsers are full screen you can’t even fake it. If, however, you want to accommodate a change in orientation, or allow for a browser which does allow you to change the window size, then you might might try the window.resize event.

In this case, since you might be replacing images more than once, it’s better to reorganise your code so that the replacement is done in a separate function:

// window.onload=function() {
    var mq=window.matchMedia;
    if(mq) {
        window.addEventListener('resize', replaceImages, false);
        replaceImages();
    }

    function replaceImages() {
        var desktop=mq('(min-width: 400px)').matches;
        var images=document.getElementsByTagName('img');
        for(var i=0;i&lt;images.length;i++) {
            if(desktop) images[i].src=images[i].src.replace(/\/mobile\//,'/desktop/');
            else images[i].src=images[i].src.replace(/\/desktop\//,'/mobile/');
        }
    }
// };

Here, the technique assumes the special desktop and mobile image folders, but it can be adapted to use the other method. The replace code has been extended to switch between desktop and larger images, and is attached to the resize event handler of the window. Finally, the function is called immediately without waiting for a resize.

Use this unique QR (Quick Response) code with your smart device. The code will save the url of this webpage to the device for mobile sharing and storage.

This excludes certain Legacy Browsers, but who cares? ↩