CSS Tips: Avoid descendant selectors and improve page rendering performance

By Anoop John | 04th September 2012 | 3 min read

Developers often overlook CSS as something very simple and not requiring a lot of thought. This leads to poorly thought out CSS rules, CSS abuse, inefficient CSS and ultimately bad rendering performance and slow page load times. Here is a simple tip to improve CSS performance and page load times - avoid descendant selectors.

Consider the following example.
#wrapper #main #content .view .view-content table.views-view-grid 
.views-field-buyitnowbutton form input.form-submit {
  padding: 1px 4px;
}

#wrapper, #main, #content, .view, .view-content are all redundant selectors in the rule as all pages with a view will have these selectors. The longer your selectors are, the larger the number of nodes that have to be evaluated per key match to see if there is a match for the rule whenever there is a match for the key. This is in addition to the fact that descendant selectors are inefficient because descendant selectors will try to match a descendant anywhere in the lineage of a node where there is a key match; i.e when there is a key match and there is no ancestor matched then nodes all the way to the root node will be evaluated to see if the rule matches.

A better way to use the same rule would be to use the child selector. Here the CSS matching engine will only evaluate 3 nodes per match on the input.form-submit key. This is assuming that the nodes are direct descendants in that order.

.views-field-buyitnowbutton > form > input.form-submit {
  padding: 1px 4px;
}

Child selectors are inefficient still because for every matching key it has to match one node each per ancestor in the rule. However it is still more efficient than descendant selectors as it would not try to match till the root node on every key match.

The descendant selector can be even more problematic when the key is a tag. For example the following rule will navigate through all the parents of all img nodes in a page.

#view_block_view img { padding-top: 30px; padding-left: 32px; }

On a Drupal site it must be noted that the number of input.form-submits would be very small on a page and the number of key matches would be very small to start with and the performance cost would not be that high. However there is still the performance cost of delivering a longer CSS rule to the browser unnecessarily. On a site with a million visitors per day. A few bytes per page can still make a significant contribution to the server load.

It is difficult to avoid using descendant selectors in a Drupal site especially when you use views and the default views templates for theming. However you should understand the problems with descendant and child selectors and try to minimize them as much as possible and even when you use them you should use them judiciously.