Bastard Child Selector
25 October 2010
The CSS child selector gets a bad rap. Most web developers rarely give it a thought and favor instead the descendant selector. But why?
What is the Child Selector?
The most simple answer: >
That’s it, the greater-than symbol. In a CSS selector it denotes a direct child and only a direct child. A selector of div > p
will match all p
elements that are direct children of a div
element.
Contrast that with the descendant selector, represented by a space. A selector of div p
matches all p
elements that are descendants of div
, no matter how far down the DOM tree the p
exists. Maybe the p
is inside an li
within a ul
within a section
within a div
? Still matches. The child selector would not.
Prior Art
Several articles have been published this year on CSS performance — how browsers read CSS selectors and render elements. Chris Coyier threw down in May with a rundown of best practices, and Jonathan Snook recently broached the subject with an explanation of why CSS doesn’t include a parent selector. The meat of these recommendations come back to a guide by Google and an aging but still relevant guide by Dave Hyatt from his Netscape days.
Read these articles, take in the lessons, and let’s look at…
An Example
Puffins
Puffins are any of three small species of auk (or alcids) in the bird genus Fratercula with a brightly coloured beak during the breeding season. These are pelagic seabirds that feed primarily by diving in the water. They breed in large colonies on coastal cliffs or offshore islands, nesting in crevices among rocks or in burrows in the soil. Two species, the Tufted Puffin and Horned Puffin, are found in the North Pacific Ocean, while the Atlantic Puffin, is found in the North Atlantic Ocean.
All puffin species have predominantly black or black and white plumage, a stocky build, and large beaks. They shed the colourful outer parts of their bills after the breeding season, leaving a smaller and duller beak. Their short wings are adapted for swimming with a flying technique under water. In the air, they beat their wings rapidly (up to 400 times per minute) in swift flight, often flying low over the ocean’s surface.
Content ripped straight from Wikipedia.
The source for the Fun Fact block might reasonably look like this:
And the CSS:
From now on let’s try this: Every time you would nonchalantly write a descendant selector, instead write it as a child selector by default. A lot of the descendants we currently write could just as easily be childs, and in some of those cases you really want them to be. If after some time and thought you realize that a descendant selector is what you want, need, and intend, go ahead and change it. Here is our revised CSS:
The web development community retains the nasty habit of descendant selectors for one reason: IE6. IE7+ and every other modern browser support the child selector. While many shops out there have stopped “supporting” the ancient beast, years of ignoring the child selector have left their mark on our collective stylesheet writing standards. Changing this habit yields a few advantages.
Performance
If we focused on CSS rendering performance and rewrote our example by biblically following the Google and Mozilla articles, we would see:
Here we have a nasty case of classitis weighing down the markup. Without going into the details of what makes classitis so heinous, let’s discard this approach.
If you read the performance articles you would know that the descendant selector will match the rightmost element, say our a
in .factoid a
, and look at every element up the tree looking for .factoid
until it finds a match or hits the body
. Using the child selector will only look one level up and stop if .factoid
isn’t found. This effects more than our factoid links. The performance savings are realized for every a
element in the document.
But here’s the deal: CSS performance is rarely an issue for modern websites. If you’re building a static site, the speed of modern devices and browsers will cover up the more egregious selectors. Web applications making constant and rapid DOM changes may see gains in slower handheld devices or older browsers. But there are better reasons to move towards more child selectors than just performance.
Preserving Defaults & Future Proofing
Let’s turn back to our example. Maybe we’re building this site for a client. The client swears factoids will follow this structure: Title, short sentence, link out. Done. In stone. So we write the CSS in our original form using descendant selectors, ship it and everything is dandy.
Six months later, the client decides they want to link up the word “Icelandic” to an article on Björk. And this is what they see:
Puffins
Puffins are any of three small species of auk (or alcids) in the bird genus Fratercula with a brightly coloured beak during the breeding season. These are pelagic seabirds that feed primarily by diving in the water. They breed in large colonies on coastal cliffs or offshore islands, nesting in crevices among rocks or in burrows in the soil. Two species, the Tufted Puffin and Horned Puffin, are found in the North Pacific Ocean, while the Atlantic Puffin, is found in the North Atlantic Ocean.
All puffin species have predominantly black or black and white plumage, a stocky build, and large beaks. They shed the colourful outer parts of their bills after the breeding season, leaving a smaller and duller beak. Their short wings are adapted for swimming with a flying technique under water. In the air, they beat their wings rapidly (up to 400 times per minute) in swift flight, often flying low over the ocean’s surface.
Woops.
I’ll admit, this is an elementary example, and a problem we can easily foresee, but think about the implications when applied to larger layout elements. Sure, your biggest client’s left sidebar may hold nothing but second-level navigation today, but who’s stopping it from holding a Twitter widget, two ad units, and a list of related posts down the road? In our scenario the client has assured us that a Factoid structure will not change. The more realistic scenario: Nobody is thinking about these things. They’re too concerned with how everything looks now.
How many times have you found yourself writing something like this?
Avoiding this mess — defining defaults, overriding them, and then re-overriding them again — would have once been solved by adding a class to the link. With our approach of using a child selector we avoid that extra markup cruft and preserve the defaults as we move down the DOM tree. Along with the potential performance gains, I hope this is enough to get you to move away from lazily pressing the space key and give the child selector a chance.
Extra Tidbit
The ultimate test of perfectly preserved default styles? Take all your main content and erase everything else. All the layout structure: erase it. Just leave your naked content, then apply your main stylesheet. Does it look exactly the same (fonts, margins, colors, etc.) as the in-page content, except now full width and aligned left? Congratulations. Personally, I’ve never been able to get there, but I keep aiming.