Oli.jp

Articles…

Don’t use IDs in CSS selectors?

Recently I came across the post by Matt Wilcox called CSS Lint is harmful, ranting about the useful free tool CSS Lint. The “Don’t use IDs in selectors” suggestion seems to have offended Matt the most, but I was surprised that many commenters also mentioned this as being a reason to avoid CSS Lint. This surprised me because smart people have been saying prefer classes to IDs for a while now. The article was light on reasons why this suggestion might be bad, but it boils down to:

  1. Performance — IDs are “the fastest way a browser can select a given element”
  2. If they’re already in the markup it’s best to use them for CSS
  3. I’ve been using IDs like forever… waddya mean I shouldn’t use them!?

Performance

It’s a common belief that ID selectors are the fastest, but this comes with a big caveat: IDs are fastest CSS selector only if they’re the key selector. What’s that? Well, while you probably read selectors from left to right, browsers read them from right to left.

#home a {…}

We’d generally read this selector as find the element with id="home", then apply these styles to every a it contains. This should be super-fast, right? After all there should only be one id="home" on the page. However, browsers read this differently: find every a element, then check if its parent element is id="home", and if not keep checking parent elements until you find it or reach <html>. That’s a lot less performant than our mental model.

As you’ve deduced, the key selector is the right-most one (a in #home a {…}). So what kind of performance difference does this actually make? I used Steve Souders’ CSS Test Creator and made three tests that I saved locally. Each test has 1,000 a elements that are selected for using individual IDs, classes or IDs with a selectors, e.g. for IDs:

#id0001 {…}
#id0002 {…}
#id0003 {…}
…
#id1000 {…}

I then reloaded the pages several times to get some average page load times (in Chrome 12, excluding initial page load)

Selector performance for 1,000 elements (in ms)
#id… .class… #id… a
90.3 91.5 104
94 93 115
91 92 101
90 91 100
90 91 100
88 92 106
89 90 102

So for 1,000 rules, IDs are about one millisecond faster than classes as a key selector. Yep, the performance difference between classes and IDs is irrelevant. The more common scenario of using an ID to ‘namespace’ an element (where the element is the key selector) is actually slower than using a class, but again the 13 millisecond difference I observed is irrelevant. This is also true for the few extra bytes it takes to add a class to something that already has an ID, e.g. changing <div id="search"> to <div id="search" class="search">.

Note this is not to say that all selectors are equal — the universal selector * {…} and some CSS 3 selectors have relatively poor performance when used as the key selector.

Results from the Measuring performance of CSS selector matching script on bitbucket.org (in Chrome 12)
% (100% is the baseline) CSS selector
~100% .class, .class:not(a), .class[a], #id, .class:nth-child(odd), element, no style
~870% [class^="a"], [title="a"]
~1020% [a], [a="b"]

However, if you’re mainly using one to two selectors (elements and/or classes), using CSS 3 selectors where appropriate is no problem. The differences from selectors are small compared to other things, and you can improve performance orders of magnitude more by optimising images and reducing HTTP requests.

IDs are brittle

In addition to styling hooks, IDs are used as fragment identifiers (an href that ends in #anchor takes you to id="anchor"), and for JavaScript’s getElementById. If you’re adding IDs for these other reasons, why not reuse them for styling? The problem is this makes your code brittle — you’ve set up dependencies between CSS and your JavaScript and/or a fragment identifier. By using classes you can decide to change to a (new) naming scheme anytime, and all you have to worry about are changing the name in your CSS and HTML.

There are also very few cases where ID-based styles will never be reused. An example is using #search for your site search box. You may later decide to add a search box at the top and bottom of your search results. Even if #search is unique in this project, you might want some quick CSS to copy&paste into a future project which also has a search box in the page footer. Even for a complex, one-off group of styles, you can generally break these down into several reusable snippets of CSS. Using classes over IDs prevents these potential problems.

Also, unlike IDs there’s no restriction on multiple classes, either using the same class more than once on the page, or using more than one class on an element. Browsers will still style IDs even if you mistakenly duplicate one, but venturing outside valid HTML sets you up for problems.

Specificity wars!

Using an ID over a class also ratchets up the specificity all the way to Darth Sidious in Andy Clarke’s Star Wars-themed guide to CSS specificity. While you might not think this is a big deal, it quickly leads to a full-on selector battle. This problem becomes worse as the CSS gets larger, and more people get involved, leading to crazy-specific selectors like .entry #content_main div.post > a {…}. This rule could probably be rewritten at least as .entry .post > a {…}, or maybe .post > a {…}, but changing it to .block-link {…} would be even better. My rule of thumb is three CSS selectors max — any more than this and it’s generally a sign of (specificity) problems.

“Tricky CSS inheritance issue — challenge accepted” by the awes Mathias Bynens (@mathias)

But but but… I’ve been using IDs like foreva!?

Styling with IDs and classes are one of the first things we learned about CSS, so it can be challenging to hear “don’t use IDs in CSS selectors”. However this industry is always changing. We occasionally need to reevaluate what works and what doesn’t, rather than just sticking with what’s served us well. We’re currently going through a massive renaissance with HTML5, CSS3, amazing JavaScript performance and rapid browser releases, so it’s a great time to do so. This is a small change, like the one from <a name=""> to <a id=""> for anchor links/fragment identifiers. However it’s important to examine the reasons rationally and decide if they’re valid, rather than rejecting them out of hand.

ARIA landmark roles

In focussing on IDs and classes I’ve completely neglected another way to select for HTML5 structural elements, like a page <header> or the main content: ARIA landmark roles. For example, if your page header is <header role="banner">, you can then select for this using the CSS header[role="banner"] {…}. They’re supported in everything from IE7 up, and you can add support for IE6 via JavaScript using Selectivizr. Rather than describing them here, read more on Ben Schwarz’ “Stepping into attribute selectors”, and Jeremy Keith’s “Landmark roles”.

Conclusion

It seems to me that there are no convincing reasons to use IDs in selectors for CSS styling¹, as CSS classes can do everything IDs can. Hopefully you’ll agree there are some good reasons not to. Think about it the next time you start a personal project or redesign your own site, and try adding a class (or ARIA landmark roles) for styling instead. Save IDs for fragment identifiers or JavaScript hooks.

If you have any feedback or comments contact me via Twitter (@boblet) or Google+ (Oli Studholme).

¹ with the exceptions of user styling on a domain-based ID, and possibly of attempting namespacing on widgets, CMS plugins and the like