Writing efficient CSS selectors

With modern browsers getting quicker with every new version number it’s easy to fall into the trap of writing inefficient code. A page will run super quick on the latest version on Chrome or Firefox, but you also have to consider older browsers and mobile devices. That shiny new web application that uses some super fancy CSS selectors may be unusable on certain devices due to its limited hardware. That’s not to say you shouldn’t be using super fancy selectors; you just have to be careful to consider your target audience, and use them in the most efficient way possible.

The first thing to note about CSS selectors is they don’t work in the way you’d expect. In the west we read a page from left to right. Reading a CSS selector, you’d expect that’s what the browser does as well. Wrong! The browser actually reads a selector from right to left (in Mozilla’s case anyway, and most likely in others too). So take the following CSS as an example:

1
2
3
4
5
body #wrapper .article ul.meta li a {
    font-weight: 700;
    text-decoration: none;
    font-family: Arial;
}

The browser first looks for all the anchor tags (called the “key” as it’s the rightmost selector), then looks at the list items, it evaluates those and throws away the results that don’t match. Next the browser moves onto elements with a class of “meta”, throwing out results that don’t match and so on… you get the idea! There’s so much redundancy in the above selector, it could easily be cut down to:

1
2
3
4
5
.meta a {
    font-weight: 700;
    text-decoration: none;
    font-family: Arial;
}

This example is much more efficient. There are less rules for the browser to evaluate, it’s much easier to read and if you apply minimal selectors across your whole stylesheet you will notice a big difference in file size. The key to writing efficient selectors is to be as specific as possible. Whatever you do don’t write this:

1
2
3
4
body * {
    margin: 0;
    padding: 0;
}

The universal selector(*) is bad (even body isn’t needed)! You are targeting every single element in the DOM and setting it’s padding and margin to zero. For a large page that could easily be thousands of elements!

I actually inspected the stylesheet for this website and went over it with a fine tooth comb. I found many additional selectors that just weren’t needed. The size of my stylesheet went from 18.3kB down to 16.5kB, a saving of 1.8kB. It doesn’t sound a lot in terms of file size, but that’s a whole lot of selectors the browser no longer has to evaluate to render the page.

Luckily there are tools available that can help you make your CSS more efficient, as well as many other areas of your website too. The first tool I’d recommend is called Page Speed, created by Google. The Page Speed extension is available on both Chrome and Firefox. Once installed you have the option to run it on any page; it will give you an overall score for that page and recommendations on how to improve it.

Google Page Speed results for Nooshu.com.
Page Speed results for this site. Note the 'Use efficient CSS selectors' drop down.

The second tool I’d recommend is Opera’s Dragonfly. Dragonfly (similar to Firebug, in name at least) is Opera’s developer toolkit, much like Web Inspector for Chrome. An awesome feature that Dragonfly has, that other toolkits don’t is “style recalculation”. Style recalculation gives you a breakdown of all the selectors that were run on the page, how long they took to evaluate and how many elements they hit along the way (hits).

Opera's Dragonfly debugger in action on nooshu.com.
Dragonfly gives you an extremely useful breakdown of what selectors were used and how long they took to evaluate.

If you look closely at the results in the image above, you will see that the selectors with the most number of hits are the ones involving the universal selector(*), as you would expect. You may also notice that most of the timings say 0.0ms which isn’t very helpful. This is due to the fact that the size of the DOM being tested is very small, and timing to 1 decimal place isn’t accurate enough to show the actual time it took to evaluate. If you were to run this test on a huge page, say the HTML5 Specification for example, you would really be able see the difference in CSS selector efficiency.

This feature of Dragonfly is very new and is still in testing. There are a few issues still to be ironed out in future releases but it’s definitely a tool to keep in your bookmarks.

A word of warning when it comes to writing efficient selectors. I found myself trying to make the selectors so efficient it was becoming quite hard to pin-point where exactly on the site they were being used. If you have a very specific area of a site you are trying to target, it is easier to read if you have the selectors starting with an ID reflecting that area. You then know for certain that the changes you make won’t affect other parts of the site. Also remember that removing “unused” selectors will affect the specificity of the rule. You could end up breaking something, as what you thought was an unused selector was actually used in overriding another rule.

Having too many descendent selectors is something that Page Speed frowns upon, but as with all things in life it’s a case of finding that happy medium. In this case it’s between efficient selectors and CSS that is easy to read and maintain (for you and other developers).

Loading

Webmentions