Write Proper CSS: Be Careful With the Descendant Selector

Evan Winston
Irrelevant Code
Published in
5 min readMar 30, 2019

This is one in a series of byte-sized tips and tricks for upping your CSS game.

In an era of frameworks, compilers, component libraries, and data-binding, most who call themselves front-end developers are making less and less time to hone their CSS skills and architecture their CSS properly. The role of CSS in shaping user experience and application flow is undeniable, yet so many client-side developers appear unwilling to touch CSS with a ten-foot pole, viewing it as a black box better relegated to “Design” or else hacking away at it piecemeal and only where necessary.

I’ve seen the same attitude affect 99.9% of my students and peers, and, to me, it smacks of the I-couldn’t-learn-coding-because-I-was-never-great-at-math-in-high-school illusion. You don’t need to be a web developer to write well-architected CSS (and designers shouldn’t be worried about code architecture), and as a front-end developer, it’s a professional responsibility to fill that gap.

One step at a time, I aim to break down those walls.

The Descendant Selector

There’s a lot of jargon to CSS of which most developers, even those who would consider themselves especially CSS-capable, are often unaware. The descendant selector is an example of one of four CSS combinators — a term we don’t need to take the time to appreciate just now. I guarantee that if you’ve written any CSS at all, you’ve had lots of experience using the descendant selector.

First, I’ll tell you what the descendant selector isn’t: >. The “>” is in fact called the child selector, selecting the direct child of a given element. No, “descendant selector” is actually just a fancy term for the space between simple CSS selectors.

Before getting to the real meat of the issue, let’s banish the one potential gotcha of identifying descendant selectors. Take a look at the code below and ask yourself how many descendant selectors are present:

body > div ul li a {
//some CSS
}

How many are there? If you guessed 5, that’s a good, solid, well-intentioned guess. But that’s incorrect. Let’s minify the code above and try again:

body>div ul li a{}

How about now?

There are three descendant selectors. The takeaway here is that, as humans, we write lots of spaces into our code that is often not parsed by the browser. So while it’s accurate to say that a descendant selector is a space, not all spaces are necessarily descendant selectors. Descendant selectors are spaces which fall between CSS selectors — it explains the relationship between two selectors. A space used to visually delineate a selector and another combinator (like “>”) is not a descendant selector; it’s just some syntactic sugar.

Why Should We Care?

Chances are, even if you hadn’t ever heard of the term combinator or descendant selector, you were intuitively aware of this, and understand what a descendant selector does (matches all elements that are descendants of a given element). Well, aside from taking some pride in your work and professionalism, the real reason we should care about descendant selectors is that they are potentially expensive!

Look at the code below. Ask yourself, what is this CSS selector selecting?

#nav a {
//some CSS
}

It’s selecting all anchors which are descendants of the element with the ID of “nav.” It’s a mouthful, sure, but probably an easy enough CSS selector for you to interpret.

Now how about this one:

a {
//some CSS
}

This is selecting all anchors.

Which CSS selector is more efficient?

While we, as humans, read CSS selectors from left to right, browsers actually read them from right to left! Consider, then, how a browser would interpret the “#nav a” selector:

First, it focuses on “a” and crawls the entire document to select every single anchor element on your page. Then, it qualifies its selection with “#nav”, and crawls through its aggregated list of every anchor on your page to select only those which are descendants of the element with the ID of “nav.”

Is that really the most efficient way to traverse your DOM elements? Let’s look at a more robust example:

body > div ul li a {
//some css
}

This selector will collect ALL anchors in your HTML, then check ALL of those anchors to filter to only those which are descendants of list-items. Then it will filter that list to those which are descendants of unordered-lists, which it will filter to those which are descendants of divs, which will be filtered to those which are children of the body element.

Just like that, what looks and reads like a very specific and focused selector becomes an expensive operation during the compilation of your site.

So what’s the answer? What is a more efficient way to isolate anchors in li’s in ul’s in div’s which sit just inside the body?

Answer: give those anchors a specific class.

.nav-link {
//some CSS
}

But Does it Really Matter?

No, probably not; for most of you. Your portfolio page or SPA probably isn’t hurt by inefficient CSS, but if you’re working on the code behind thousands of DOM elements, or code which is constantly and reactively updates, or code which utilizes expensive CSS properties (that’s another discussion), it could definitely make a very perceivable difference.

Also, we’re professionals, right?

More to the point, consider this the next time you’re feeling frustrated with a class-heavy CSS library like Bootstrap or Bulma. I’ve seen dozens and dozens of students let themselves get lost in class-rich tools like these, wondering why an anchor element six layers deep into their navbar needs three class names just to apply the right background color. Part of that is writing-lean-responsive-predictive-css-is-friggen-hard-try-writing-it-yourself-for-a-change, part of it is for granular-level customization, and much of it is for performant CSS!

TLDR

The descendant selector, often seen as a gateway to more specific and less-costly CSS selectors, can in fact easily turn around and slow down your code. Be careful with it.

This isn’t a call-to-action to do away with superfluous descendant selectors, or all element selectors in your code; this is merely a reminder of a lurking principle to be aware of, a call-to-action to inform your own code-writing here on out with one more piece of data to guide you towards the writing of clean, performant, effective, well-structured CSS.

Evan is an illustrator, developer, designer, and animator who tells stories in any which way he can. When he’s not branding businesses or building front-end apps; he’s illustrating children’s books, painting for tabletop games, animating commercials, or developing passion projects of his own.

--

--