Combining media queries and JavaScript

On Tuesday Jason Grigsby challenged the conventional view that media queries are all we need to make a website mobile-friendly. Although he’s right when he points out some serious problems, I do not think that media queries are the “fool’s gold,” as Jason says. The message seems to be more that media queries alone are not enough to make your sites mobile-friendly. An additional component is required.

To recap briefly, this is a media query:

.sidebar {
	float: right;
	width: 250px;
}

@media all and (max-device-width: 600px) {
	.complicatedFunctionality {
		display: none;
	}
	.sidebar {
		float: none;
		width: auto;
	}
}

The media query asks whether the width of the device is 600px at maximum. If it is, the special styles are executed. This usually comes down to hiding advanced functionalities that do not make sense on mobile or take too much bandwidth, while it can also be used to change the site’s grid from horizontally oriented to vertically oriented; for instance by placing a sidebar not right of the main content, but below it.

Technically, the .complicatedFunctionality and .sidebar declarations are added to the style sheet only when the query returns true, i.e. the device is less than 600px wide.

Now this works. Better still, media queries are the ideal way of adapting your design to different screen resolutions. Still, they are not the be-all-end-all of making your website mobile-friendly.

Jason identifies two main problem areas:

In other words, media queries do not stop the browser from downloading assets that will not be used on a mobile phone. And with bandwidth at a premium, this is a serious problem.

He concludes:

CSS media queries are a tool, but they are not a silver bullet.

I mostly, but not entire agree. Media queries are silver bullets when it comes to pure CSS. Restricting the width of your site, moving sidebars and main navigations elsewhere, media queries can do all that and more.

The trick, however, is that a pure CSS approach is not enough. In addition we need a JavaScript that also reads out the media queries and uses the data to decide whether to download the complicated mapping script, whether to download the low-source or the full-source images, or possibly none.

As far as I know there is no direct access to media queries from JavaScript. You can’t read out whether the example media query above has fired or not.

JavaScript pairing

Fortunately, there is a pretty safe way of using JavaScript in conjunction with media queries. It turns out that all browsers I tested so far have paired the width and device-width media queries with the values of document.documentElement. clientWidth and screen.width, respectively.

This is a general rule. All mobile browsers that support media queries exhibit these pairings. It’s hard to believe, but I haven’t found any exceptions yet — and rest assured that I searched for them, because I could not believe that it would be this simple. And I will continue to keep an eye on this and report problems as soon as I find them.

One caveat, though: although the pairing exists in all browsers, some browsers report incorrect values for document.documentElement. clientWidth and screen.width. However, these browsers will also fire media queries based on these incorrect values, so the net result remains that both media query and script are executed, although at the wrong time. See the viewport compatiblity table for the gory details.

Therefore, if we want a JavaScript component that fires when the example media query above is triggered, we simply do:

if (screen.width < 600) {
	// don’t download complicated script
	// use low-source images instead of full-source ones
}

I think it’s best to reverse the script logic:

if (screen.width >= 600) {
	// download complicated script
	// swap in full-source images for low-source ones
}

If you want to use width instead, do this:

@media all and (max-width: 900px) {
	// styles
}

if (document.documentElement.clientWidth < 900) {
	// scripts
}

Thus it is quite possible to pair a JavaScript routine with your media queries, and use it to decide which assets you should and should not download.

When these scripts are added to media queries, we’re a whole lot closer to making one website that reacts to a mobile (or rather, a narrow-screen) environment both in its CSS and in its asset management.

This is the blog of Peter-Paul Koch, mobile platform strategist, consultant, and trainer. You can also follow him on Twitter.
Atom RSS

I’m around at the following conferences:

(Data from Lanyrd)

Categories:

Monthlies:

Comments

Comments are closed.

1 Posted by Trygve Lie on 5 August 2010 | Permalink

Over at Opera Dev there is a small script from 2007 which has an interesting approach doing almost the same: http://dev.opera.com/articles/view/media-query-library/

In that approach you end up with something like this:

var test = testMediaQuery('tv and (max-height: 400px)');

2 Posted by Jasper van der Kamp on 5 August 2010 | Permalink

This is obvious. I have been doing this myself. When you use media queries to make your sites mobile friendly, you'll soon find out that (some of) your scripts also don't fit on mobile very well.

3 Posted by Vladimir Carrer on 5 August 2010 | Permalink

I tried to resolve this issue myself I used pure CSS for the modern browsers, conditional comments for IE and javascript for Firefox lower then 3.5. With this solution the browser theoretically will load only one CSS and ignore all the rest of CSS. http://www.vcarrer.com/2010/07/bulletproof-css3-media-queries.html

4 Posted by Michael on 5 August 2010 | Permalink

In Google Chrome (at least v.5, I don't know about other browsers or versions) you can test media queries like this:

alert(window.media.matchMedium('(device-width: 1680px)'));

Ref:

http://www.w3.org/TR/cssom-view/#media

5 Posted by ppk on 5 August 2010 | Permalink

Thanks for all the interesting technical pointers. Please keep them up.

However, the trick I propose in this article is *certain* to work in all browsers, while I still have to test the rest.

But obviously things are moving along nicely here.

6 Posted by Andrew Hedges on 5 August 2010 | Permalink

Thanks for this. I read Jason's article and thought the title should have been "Expecting CSS Media Queries to do things they weren't designed to do is Fool's Gold." For many things, as you point out, they're the exact right tool.

7 Posted by Justin Long on 5 August 2010 | Permalink

Another thing to do is to create something like:

Welcome to the Mobile version!

And then do a get computed style (branched of course for IE), and set a flag in your javascript. Something along the lines of isDesktop.

This would also allow you to set mobile functionality with javascript for a weak client (old desktops that can't handle as much).

8 Posted by Justin Long on 5 August 2010 | Permalink

EDIT:

The above should be written as:

<span class="mobile">Welcome to the Mobile version!</span>

9 Posted by Paul Connolley on 5 August 2010 | Permalink

This is something I’ve been thinking about since reading the article by Jason.

One of my ideas was to have an element in the HTML document that you apply different values to the z-index within different media queries.

I knocked something up here to demonstrate:

http://shunuk.co.uk/tests/media-query-snooper.htm

Apologies for the rather random looking media queries in the example but I’ve been experimenting on set of good generic widths for media queries and so they may seem a little crazy.

10 Posted by Paul Connolley on 5 August 2010 | Permalink

@Michael’s reference to window.media.matchMedium() looks interesting. If that were to be implemented across the major browsers that would definitely remove the need for any javascript hackery (although it’s nice to do a little bit of javascript hackery every now and then.)

11 Posted by Jason Grigsby on 5 August 2010 | Permalink

Thanks PPK for following up on this.

A couple of quick thoughts on javascript as a companion to css media queries:

* It doesn't address the size of the html doc itself and the necessity for the uncompressed size of the doc to be as small as possible to get cached.

* It doesn't address the idea that you may find that your mobile site needs to do different things to address user context.

* Not all mobile browsers support media queries.

But fair enough. Perhaps you can build a successful mobile site using media queries and javascript.

Doing so is more complex than any of the previous articles--or in particular the Big Show podcast--make it out to be.

You need to refactor your CSS and JS so that as @PPK suggests the mobile version is first and desktop is layered on top. Not a single article on the topic addressed this.

More importantly, if you have any sort of CMS and that CMS is being used by multiple people, you're likely going to need to figure out how you're handling image resizing and how you're ensuring markup that won't work for mobile doesn't end up in the system.

12 Posted by alexander farkas on 5 August 2010 | Permalink

The script @ Opera isn't working in other browsers than opera.

You find a Script @ http://plugins.jquery.com/project/MediaQueries. The main goal was to support simple mediaqueries. But there is also a way to test a medium-string with the method $.testMedia.

I.E.: $.testMedia('tv and (max-height: 400px)');

13 Posted by Brad Czerniak on 5 August 2010 | Permalink

Media Queries aren't necessarily declared at the file level, so you can't rely on conditional comments to quarantine your scripts from unsupported browsers.

Any CSS Media Query reliant script should also do a feature check to ensure the browser supports it.

An easy way: set the width of a hidden element, then set it to something different using a query that always returns true. Then, your conditional can be based on the width as set in the DOM.

Overall a useful article. Thanks.

14 Posted by stuart robson on 5 August 2010 | Permalink

Was thinking about this last night / this morning and found a couple of jquery scripts that would facilitate the idea, just don't know how to code it to make it work - http://bit.ly/bpNU8Y is the stackoverflow question. Wish I knew jquery more than I do then I could perhaps work it out myself as this would be a 'game changer'

Stop, Carry on :-)

15 Posted by Frans on 5 August 2010 | Permalink

@14, Stuart:

I'd think jQuery is exactly the kind of thing you'd want to avoid on the mobile version?

16 Posted by Rik on 6 August 2010 | Permalink

Although I don't have a better solution, I have to say that this snippet assumes that a small screen means a slow network and vice versa.

I can be on a personal wifi on my mobile and I'll have a fast network (not fast computing though). And I can also use a crappy public wifi or 3G key on a laptop.

There is no simple way to test for that but with your snippet, I can't use the mobile version when I'm on my laptop. Sure, I can reduce the size of my browser because I know the trick but your average user won't know that.

17 Posted by ppk on 6 August 2010 | Permalink

Rik,

You raise a very valid point that will have to be solved in the future; preferably by allowing JS to read out the connection speed.

However, even if we know the connection speed we might still want to leave out a mapping components due to lack of space, and we don't need to download the associated scripts. Similarly, if we decide that the images have to take less space on a mobile browser we can safely download the low-source variants.

So yes, connection speed definitely matters, but it's not the *only* factor. Available space is just as important.

18 Posted by ppk on 6 August 2010 | Permalink

Jason,

Size of the HTML doc can be regulated by downloading the heavy components by Ajax after you've determined the browser has enough space available.

Different things to address: yes, it does help to solve that. Based on screen size you can decide whether to include, I don't know, a direct route map from your current location or a generic map. (That's not fundamentally sound; a user could narrow his desktop browser window to 400px and get the mobile version, but hey, we have to start SOMEwhere.)

Not all mobile browsers support media queries. Correct. My take: screw them. If they don't give us the tools for progressive enhancement, that's their problem, and not ours. The mobile space gives us more freedom than the desktop space in that regard.

19 Posted by Roland van Ipenburg on 6 August 2010 | Permalink

Things make more sense if we don't regard every new recommendation as some hack for whatever issues generic web developers incidentally have at the moment. Media queries are nice, but don't expect to scale a single generic HTML source sensibly across every possible medium just because the specs don't disallow it. CSS media queries could for example be useful in embedded environments which don't even have a JavaScript engine, but would benefit from a standard HTML+CSS interface on a limited range of display formats. But that doesn't mean it's supposed to be just as useful in every other situation we're occupied with.

20 Posted by Gerben on 6 August 2010 | Permalink

> Even though images might be hidden from mobile browsers, or low-source ones should be used, the browser still downloads the full-source variants.

I though browsers we smart enough to not load images that are not visible.
So I made a test page, and did some tests in FireFox, Safari and Safari on iPhone. I would have loved to test on Opera too, because I remember reading something about them not loading images that aren't visible, but I don't have any installation of it nearby.

The conclusions are
+ Images in the html are always loaded, even if set to display none, or are in a container that has display none. (also if using inline styles)
+ In safari and safari mobile background images are loaded even if the element itself is display none. Same result when display none was inside a media query. Firefox doesn't load the image.
+ Background images attached to elements that are inside a element with display none are NOT loaded.
+ background images attached to styles that aren't applied are not loaded.

So for iPhone you should not only set a element to display none, but also set background to none.

It seems (mobile) browser could get a bit smarter about which images not to load.

21 Posted by Jason Grigsby on 6 August 2010 | Permalink

@ppk wrote:

"Not all mobile browsers support media queries. Correct. My take: screw them. If they don't give us the tools for progressive enhancement, that's their problem, and not ours. The mobile space gives us more freedom than the desktop space in that regard."

That's a fair position to take. I even mentioned that as such in my original article.

I believe the decision should be an informed decision based on the demographics of your customers, not on the basis of technical support for media queries.

Some of the articles promoting media queries have smartphone blindness. It's not as bad as iPhone blindness which you've called out in the past, but it is still an affliction.

All web development and design is a series of compromises. The key in mind my is to be cognizant of the compromises you're making.

22 Posted by Web Tasarim | John Alden on 8 August 2010 | Permalink

Thanks man... This post just makes me love CSS more. I didn't even know you could do media queries through CSS. I always just used JS so far.

23 Posted by Andrea Giammarchi on 10 August 2010 | Permalink

Interesting post as usual, but if I can say my opinion, nowadays mobile browsers can handle well written website quite well. Do we really need to add that much on top? The risk is to slow down Netbooks considering them powerful PCs because of the screen size.

Moreover, some powerful Laptop/PC may have simply 1280px width but the browser has the bookmark or some other bar on the side, easy to reach 900px then.

I am sure we will have better solutions soon, but again, why on earth we have to put more if we can offer the same with less? ( faster/slicker Web anybody? )

24 Posted by Paul Irish on 18 August 2010 | Permalink

I've written a matchMedium() compat shim for browsers that don't yet implement it:
http://gist.github.com/513213

It's in the CSSOM spec, so it's on the way to be implemented everywhere... i hope. :)

25 Posted by Brian Campbell on 20 August 2010 | Permalink

One problem with using media queries, and the JavaScript mentioned in the article, for mobile sites is that it gives you no way to allow the user to turn the mobile view off (without, at least, significantly more work in JavaScript setting cookies and manipulating the CSS programatically). Most good mobile sites have a link to the full version, in case you have a capable browser and the mobile site lacks a feature that you need.

I've encountered some mobile sites that eliminated all Flash embeds, assuming that Flash doesn't work on mobile devices, but I'm running Froyo with Flash 10.1 and would like to watch some of the videos involved. When they provide a link to the full site, I can then just tap that, find the video on the full site, and play it, but with this media query method, I'm stuck on the mobile version unless the developer put in a good deal more work to support switching.

26 Posted by Scott Jehl on 25 August 2010 | Permalink

Paul - nice fork of the media query function! :)

Brian mentioned switching between desktop and mobile versions...

In the file loading mechanism we've implemented in EnhanceJS, you can specify media types & queries for loading both CSS and JS files (internally, it uses the gist Paul forked above).

There are a number of benefits to this for mobile: for one, when a media query doesn't apply, an associated JS file simply won't be requested at all, which begins to address the mobile bandwidth concerns in the Fool's Gold article. We can also use exclusive media queries for desktop-only assets while still supporting browsers that don't understand media queries at all (like IE).

Using media queries this way also allows us to toggle between desktop & mobile versions with a toggleMedia() method. To see it in play, scroll to the bottom of http://filamentgroup.com and click "mobile version" or "desktop version". Same markup for both experiences, but by applying different JS and CSS through exclusive media queries, the experience feels fairly catered to the device.