--- comments: true date: 2011-09-17 10:58:24 layout: post slug: writing-efficient-css-selectors title: Writing efficient CSS selectors wordpress_id: 3144_ meta: "CSS selectors, and their combinations, can be fast or slow! And while hopefully imperceptible, it’s definitely worth knowing about." categories: - Web Development tag: - CSS - CSS Selectors - Performance --- Efficient CSS is not a new topic, nor one that I really need to cover, but it’s something I’m really interested in and have been keeping an eye on more and more since working at [Sky](/case-studies/bskyb/). A lot of people forget, or simply don’t realise, that CSS can be both performant and non-performant. This can be easily forgiven however when you realise just how little you can, err, realise, non-performant CSS. These rules only _really_ apply to high performance websites where speed is a feature, and 1000s of DOM elements can appear on any given page. But best practice is best practice, and it doesn’t matter whether you’re building the next Facebook, or a site for the local decorator, it’s always good to know… ## CSS selectors CSS selectors will not be new to most of us, the more basic selectors are type (e.g. `div`), ID (e.g. `#header`) and class (e.g. `.tweet`) respectively. More uncommon ones include basic pseudo-classes (e.g. `:hover`) and more complex CSS3 and ‘regex’ selectors, such as `:first-child` or `[class^="grid-"]`. Selectors have an inherent efficiency, and to quote [Steve Souders](http://stevesouders.com/), the order of more to less efficient CSS selectors goes thus: 1. ID, e.g. `#header` 2. Class, e.g. `.promo` 3. Type, e.g. `div` 4. Adjacent sibling, e.g. `h2 + p` 5. Child, e.g. `li > ul` 6. Descendant, e.g. `ul a` 7. Universal, i.e. `*` 8. Attribute, e.g. `[type="text"]` 9. Pseudo-classes/-elements, e.g. `a:hover` Quoted from Even Faster Websites by [Steve Souders](http://stevesouders.com/) It is important to note that, although an ID is technically faster and more performant, it is barely so. Using Steve Souders’ CSS Test Creator we can see that [an ID selector](http://stevesouders.com/efws/css-selectors/csscreate.php?n=1000&sel=%23id&body=background%3A+%23CFD&ne=1000) and [a class selector](http://stevesouders.com/efws/css-selectors/csscreate.php?n=1000&sel=.class&body=background%3A+%23CFD&ne=1000) show very little difference in reflow speed. In Firefox 6 on a Windows machine I get an average reflow figure of 10.9 for a simple class selector. An ID selector gave a mean of 12.5, so this actually reflowed slower than a class. **The difference in speed between an ID and a class is almost totally irrelevant.** A test selecting on a type (``), rather than a class or ID, gave [a much slower reflow](http://stevesouders.com/efws/css-selectors/csscreate.php?n=1000&sel=a&body=background%3A+%23CFD&ne=1000). A test on a heavily overqualified descendant selector gave [a figure of around 440](http://stevesouders.com/efws/css-selectors/csscreate.php?n=1000&sel=div+div+div+div+div+div+a&body=background%3A+%23CFD&ne=1000)! From this we can see that the difference between IDs/classes and types/descendants is fairly huge... The difference between themselves is slight. **N.B.** These numbers can vary massively between machine and browser. I _strongly_ encourage you to run/play with your own. ### Combining selectors You can have standalone selectors such as `#nav`, which will select any element with an ID of ‘nav’, or you can have combined selectors such as `#nav a`, which will match any anchors within any element with an ID of ‘nav’. Now, we read these left-to-right. We see that we’re looking out for `#nav` and then any `a` elements inside there. Browsers read these differently; **browsers read selectors right-to-left**. Where we see a `#nav` with an `a` in it, browsers see an `a` in a `#nav`. This subtle difference has a _huge_ impact on selector performance, and is a very valuable thing to learn. For an in-depth reason as to why they do this see [this discussion on Stack Overflow](http://stackoverflow.com/questions/5797014/css-selectors-parsed-right-to-left-why). It’s more efficient for a browser to start at the right-most element (the one it _knows_ it wants to style) and work its way back _up_ the DOM tree than it is to start high up the DOM tree and take a journey _down_ that might not even end up at the right-most selector--also known as the _key_ selector. This has a very significant impact on the performance of CSS selectors... ## The _key_ selector The key selector, as discussed, is the right-most part of a larger CSS selector. This is what the browser looks for first. Remember back up there we discussed which types of selector are the most performant? Well whichever one of those is the key selector will affect the selector’s performance; when writing efficient CSS it is this key selector that holds the, well, key, to performant matching. A key selector like this:
#content .intro {}
Is probably quite performant as classes are an inherently performant selector. The browser will look for all instances of `.intro` (of which there aren’t likely to be many) and then go looking up the DOM tree to see if the matched key selector lives in an element with an ID of ‘content’. However, the following selector is not very performant at all:
#content * {}
What this does is looks at _every single_ element on the page (that’s _every_ single one) and then looks to see if any of those live in the `#content` parent. This is a very un-performant selector as the key selector is a very expensive one. Using this knowledge we can make better decisions as to our classing and selecting of elements. Let’s say you have a massive page, it’s enormous and you’re a big, big site. On that page are hundreds or even thousands of `
`s. There is also a small section of social media links in a `