views:

44

answers:

1

While trying to reduce the HTML size of a web page, I've come across suggestions by Google and the PageSpeed Firefox Add-On regading efficiency of CSS selectors that (almost) made me reconsider the changes:

http://code.google.com/intl/de-DE/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors

Specifically, descendant selectors are great for selecting a whole block (e.g. DIV) using an ID or CLASS attribute and then keeping all of its child elements free of CLASS/ID attributes. But if the order of traversal for applying the rules is as described by Google, they should not be used:

Descendant selectors are inefficient because, for each element that matches the key, the browser must also traverse up the DOM tree, evaluating every ancestor element until it finds a match or reaches the root element. The less specific the key, the greater the number of nodes that need to be evaluated.

I very much doubt that browsers use such an inefficient order of traversal, surely they would only process subtrees of elements that match the top selector component, i.e. in #foo span {...} only elements below #foo should be checked and not every single span. Can anyone who has looked at recent browser code confirm/deny this?

The second questionable suggestion is about overly qualified selectors:

ID selectors are unique by definition. Including tag or class qualifiers just adds redundant information that needs to be evaluated needlessly.

If ID selectors are unique by definition, why would browsers need to check the redundant information? I know that they do because for example,

div#foo { color: black; } #foo { color: white; }

will result in black text in a <div id=foo>, but a) it should not be done(? W3C reference needed) and b) I don't see why it would be noticeably slower when it results in a simple O(1) check of the element's tag name.

Can anyone on good terms with modern browsers' source code shed some light on these claims? Since most modern sites use descendant selectors (including SO) and they have clear advantages, I'd very much like to use them...

Edit:

I've experimented a bit with generated pages and it seems that the browsers' handling of descendant selectors is indeed pitiful:

A page consisting of (abbreviated):

#top a {text-decoration: none;}

#foo1 a.foo {color: red;}

#foo2 a.foo {color: red;}

[... repeated 10000 times]

<body id=top>

<div>...[nested 50 times]<a href=foo>bla</a></div>[...]

[previous line repeated 10000 times]

(basically 10000 lines with 50 nested divs each to traverse till the root node and 1 selector that matches out of 10000)

loads and renders (time till window.onload() executes) in 2.2 seconds using Safari 5 and just under 10 seconds with Firefox 3.6.10.

When the .foo class selector is removed from the non-applying rules, the page takes around 200 seconds with Safari 5 and 96 seconds with Firefox 3.6.10. This illustrates how badly the descendant selectors are implemented (in that case, each of the 10000 rules probably causes a traversal till #top, where the rule fails).

How do child selectors fare? #foo > span > div > div > div > div > div a {color: red;} (also never matches but forces traversal of 6 parent nodes) takes 27 seconds with Safari 5 and 31 seconds with Firefox 3.6.10.

Conclusion

Descendant and child selectors both suck currently on major browsers. It's still better to add ugly class/id attributes to all your styled tags if you care about speed, at least for very common HTML tags (such as a, img, div etc.).

+3  A: 

Have a look at this recent post by Jonathan Snook: http://snook.ca/archives/html_and_css/css-parent-selectors

You'll see how browsers evaluate expressions and the reasoning behind why certain selectors are inefficient.

A relevant quote from the post:

CSS gets evaluated from right to left.

To determine whether a CSS rule applies to a particular element, it starts from the right of the rule and works it's way left.

If you have a rule like body div#content p { color: #003366; } then for every element—as it gets rendered to the page—it'll first ask if it's a paragraph element. If it is, it'll work its way up the DOM and ask if it's a div with an ID of content. If it finds what it's looking for, it'll continue its way up the DOM until it reaches the body.

By working right to left, the browser can determine whether a rule applies to this particular element that it is trying to paint to the viewport much faster. To determine which rule is more or less performant, you need to figure out how many nodes need to be evaluated to determine whether a style can be applied to an element.

Moin Zaman
Thanks for the link, while it does not provide any evidence that browsers actually do this nowdays, it is another data point. What I don't understand is why browsers would process the DOM in such an inefficient way when they can e.g. maintain a running list of ancestor node IDs/classes and applicable css rules while traversing the document to avoid checking rules with descendant (should be called ancestor?) selectors. It might simplify DOM manipulation, but it's bound to sabotage adoption of modern, more beautiful/readable css selector usage, useful esp. for white label sites ...
mjy
Snook works at Google and is a long time web Guru, if he says it, I believe him that browser do process the DOM as he's said.
Moin Zaman