Guidelines for writing efficient CSS selectors
First, let’s see how the style system works.
“The style system matches a rule by starting with the rightmost selector and moving to the left through the rule’s selectors. As long as your little subtree continues to check out, the style system will continue moving to the left until it either matches the rule or bails out because of a mismatch.” – David Hyatt
When I first read this, I got one of those Aha! Moments, because I always thought that CSS selectors was interpreted left to right.
1 | #foobar a { color: black; } |
If we take our example from above, this would mean that instead of just checking for anchor elements inside foobar, the browser will lookup all anchors in the page. Instead of just checking each anchor’s parent, the browser will crawl the document tree, trying to find an ancestor with the ID foobar and it will continue up the tree of ancestors until it reaches the document root.
These guidelines was put forward by David Hyatt, a Safari and WebKit architect.
Avoid universal rules
It is recommended that you use ID, class and tag selectors.
Don’t qualify ID-categorized rules with tag names or classes
Since there can only be one element in the page with a given ID, there is no need to add extra qualifiers.
1 2 | NO – div#foobar YES - #foobar |
Don’t qualify class –categorized rules with tag names
Instead of qualifying class selectors for specific tags, extend the class name to be specific to the use case.
1 2 | NO – div.foobar
YES - .foobar |
Try to put rules into the most specific category you can
Don’t be tempted to build long selectors, because it can be the biggest slowdown in the system. It is better to be specific and add a class to the appropriate elements.
1 2 | NO – ul li a YES – .list-anchor |
Avoid descendant selectors
Watch out for this one, this is really an expensive operation and should be avoided. This can often be replaced with a child selector.
Avoid tag-child selectors
Don’t use a child selector that is based on a tag, it is better to use a class that is associated with the tag; this can really improve the matching time.
1 2 | NO - #foobar > li > a YES - .foobar-anchor |
Question all usages of the child selector
In general you should be careful about the child selector and it would be a good idea to avoid them.
Rely on inheritance
Learn which properties are inherited, and avoid rules that specify these inherited styles.
Should I use them?
Well, the answer is; it depends. First you need to be sure that you are focusing on the right problem. Not all pages will be affected, it basically comes down too how much stuff you put into it. The more complex, the greater the chance is that your page will be slowed down significantly (a couple of seconds).
Second, not all complex selectors affect performance, because it comes down to the key selector, the rightmost argument.
Consider this
1 | Div div div a.myclass {…} |
This can be ok, because our key selector is a.myclass, which might only match one or few elements.
1 | a.myclass * {…} |
However, this is really bad because it will check every element to see if it is descendant of an anchor with the given class.
Final Word
You might be thinking what about the CSS quires, which I use with my favourite javascript library. Those libraries read left to right because that is the way the DOM APIs work. So the guidelines mentioned above is only how the browser will render CSS, so when you are working with stylesheets, focus on right to left.
And be sure to focus on the once that are the most expensive once.
Further reading
Faster HTML and CSS: Layout Engine Internals for Web Developers