CSS Combinator Precedence

CSS combinator precedence?

No, there is no notion of precedence in combinators. However, there is a notion of order of elements in a complex selector.

Any complex selector can be read in any direction that makes sense to you, but this does not imply that combinators are distributive or commutative, as they indicate a relationship between two elements, e.g. ancestor descendant and previous + next. This is why the order of elements is what matters.

According to Google, however, browsers implement their selector engines such that they evaluate complex selectors from right to left:

The engine [Gecko] evaluates each rule from right to left, starting from the rightmost selector (called the "key") and moving through each selector until it finds a match or discards the rule.

Mozilla's article, Writing Efficient CSS for use in the Mozilla UI has a section that describes how their CSS engine evaluates selectors. This is XUL-specific, but the same layout engine is used both for Firefox's UI and pages that display in Firefox's viewport. (dead link)

As described by Google in the above quote, the key selector simply refers to the right-most simple selector sequence, so again it's from right to left:

The style system matches rules by starting with the key selector, then moving to the left (looking for any ancestors in the rule’s selector). As long as the selector’s subtree continues to check out, the style system continues moving to the left until it either matches the rule, or abandons because of a mismatch.

Bear in mind two things:

  • These are documented based on implementation details; at heart, a selector is a selector, and all it is intended to do is to match an element that satisfies a certain condition (laid out by the components of the selector). In which direction it is read is up to the implementation; as pointed out by another answer, the spec does not say anything about what order to evaluate a selector in or about combinator precedence.

  • Neither article implies that each simple selector is evaluated from left to right within its simple selector sequence (see this answer for why I believe this isn't the case). What the articles are saying is that a browser engine will evaluate the key selector sequence to figure out if its working DOM element matches it, then if it does, progress onto the next selector sequence by following the combinator and check for any elements that match that sequence, then rinse and repeat until either completion or failure.


With all that said, if you were to ask me to read selectors and describe what they select in plain English, I would read them from right to left too (not that I'm certain whether this is relevant to implementation details though!).

So, the selector:

a > b ~ c d

would mean:

Select any d element

that is a descendant of a c element

that is a sibling of, and comes after, a b element

that is a child (direct descendant) of an a element.

What's the precedence of the + operator in CSS selectors?

Every sequence of simple selectors and combinators is read by browsers from right to left, in a linear fashion. Combinators do not affect the ordering in any way. The rightmost selector, after the last combinator if any, is known as the key selector (see the reference links below for more), and that identifies the element that the rule applies to (also known as the subject of the selector, although note that the key selector may not always represent the subject of the selector, since different applications implement selectors differently).

The selector #id1 #id2 + #id3 means

Select element #id3

if it directly follows as a sibling of #id2

that is a descendant of #id1.

A DOM structure in which #id3 would match the selector would look like this:

#id1
... any level of nesting
#id2
#id3

While #id1 + #id2 #id3 means

Select element #id3

if it is a descendant of #id2

that directly follows as a sibling of #id1.

And a DOM structure in which #id3 would match the selector would look like this:

#id1
#id2
... any level of nesting
#id3

Notice the difference in the position of element #id2 in this DOM structure, as compared to the one above.

There isn't much of a precedence issue here since the descendant and sibling combinators go in different directions in the DOM. Both selector sequences read right to left either way.

Related answers:

  • CSS combinator precedence?
  • Are parentheses allowed in CSS selectors?
  • CSS Adjacent Selector / Meaning

CSS child selector higher precedence than class selector?

div.form-square > div consists of 1 class selector + 2 type selectors (plus a child combinator).

.seven-col consists of 1 class selector.

The number of class selectors is equal, so the comparison is done in type selectors. The first selector has more type selectors so it is more specific.

Specificity is based on the number of each kind of selector in the entire thing, not for the part on the right hand side of the rightmost combinator.

(NB: The first example also has what CSS 2 calls a child selector, but that doesn't count towards specificity and describes a relationship between elements rather than a feature of an element, which probably why CSS 3 is renaming it to the child combinator).

CSS selector precedence - why is td higher than a pseudo-selector?

It isn't more specific. It matches a different element.

td { color: #669; } overrides the default stylesheet (which is probably something like td { color: inherit; }) because author stylesheets override browser stylesheets.

If you want to match the td when the tr is hovered, then you need to mention the td in the selector (e.g. with a descendant combinator).

tbody tr:hover td {}

Comma in CSS, multiple selectors using the same CSS

.Resource table.Tbl td, .Resource table.Tbl2 td { /* some css*/ }

You should add the full ancestor path for both rules. Not just where you see differences.

In which direction do selector engines read, exactly?


However I've read recently that most CSS selector engines read from right to left, in which case wouldn't the first example actually be slower?

Which way to CSS selector engines read in general? Left to right or right to left? And if they generally read right to left could someone please offer me an explanation as to why (I can't see how it makes sense to read right to left in terms of a selector engine)?

Frankly, it's nigh impossible to tell which selector will be slower in a given browser, much less across browsers. Performance tends to fluctuate and be unpredictable, especially at such microscopic scales and with unpredictable document structures. Even if we talk about theoretical performance, it ultimately depends on the implementation.

Having said that, as shown in Boris Zbarsky's answer to this other question and in Guffa's answer to yours, a typical browser (this is currently true of all major layout engines) takes an element and evaluates all the candidate selectors to see which ones it matches, rather than finding a set of elements that match a given selector. This is a subtle but very important difference. Boris offers a technical explanation that's not only incredibly detailed, but also authoritative (as he works on Gecko, the engine used by Firefox), so I highly suggest reading it.

But I thought I should address what seems to be another concern in your question:

As the selector engine would simply find every element with a class of name, and then have to identify which of those were divs?

As well as Patrick McElhaney's comment:

The linked question explains why selectors are read right-to-left in general, so #foo ul.round.fancy li.current is read li.current, ul.round.fancy, #foo, but is it really read right-to-left within each element (.current, li, .fancy, .round, ul, #foo)? Should it be?

I have never implemented CSS, nor have I seen how other browsers implement it. We do know from the answers linked above that browsers use right-to-left matching to walk across combinators within selectors, such as the > combinators in this example:

section > div.second > div.third

If an element isn't a div.third, then there is no point checking if its parent is a div.second whose parent is a section.

However, I don't believe that this right-to-left order drills all the way down to the simple selector level. In other words, I don't believe that browsers use right-to-left evaluation for each part of a simple selector sequence (also known as a compound selector) within the right-to-left evaluation across a series of compound selectors separated by combinators.

For example, consider this contrived and highly exaggerated selector:

div.name[data-foo="bar"]:nth-child(5):hover::after

Now, there's no guarantee a browser will necessarily check these conditions for an element in the following order:

  1. Is the pointer over this element?
  2. Is this element the 5th child of its parent?
  3. Does this element have a data-foo attribute with the value bar?
  4. Does this element have a name class?
  5. Is this a div element?

Nor would this selector, which is functionally identical to the above except with its simple selectors jumbled around, necessarily be evaluated in the following order:

div:hover[data-foo="bar"].name:nth-child(5)::after
  1. Is this element the 5th child of its parent?
  2. Does this element have a name class?
  3. Does this element have a data-foo attribute with the value bar?
  4. Is the pointer over this element?
  5. Is this a div element?

There is simply no reason that such an order would be enforced for performance reasons. In fact, I'd think that performance would be enhanced by picking at certain kinds of simple selectors first, no matter where they are in a sequence. (You'll also notice that the ::after is not accounted for — that's because pseudo-elements are not simple selectors and never even enter into the matching equation.)

For example, it's very well-known that ID selectors are the fastest. Well, Boris says this in the last paragraph of his answer to the linked question:

Note also that there are other optimizations browsers already do to avoid even trying to match rules that definitely won't match. For example, if the rightmost selector has an id and that id doesn't match the element's id, then there will be no attempt to match that selector against that element at all in Gecko: the set of "selectors with IDs" that are attempted comes from a hashtable lookup on the element's ID. So this is 70% of the rules which have a pretty good chance of matching that still don't match after considering just the tag/class/id of the rightmost selector.

In other words, whether you have a selector that looks like this:

div#foo.bar:first-child

Or this:

div.bar:first-child#foo

Gecko will always check the ID and the class first, regardless of where it is positioned in the sequence. If the element doesn't have an ID and a class that matches the selector then it's instantly discarded. Pretty darn quick if you ask me.

That was just Gecko as an example. This may differ between implementations as well (e.g. Gecko and WebKit may do it differently from Trident or even Presto). There are strategies and approaches that are generally agreed upon by vendors, of course (there isn't likely to be a difference in checking IDs first), but the little details may differ.

Selecting multiple adjacent siblings (and their children)

Why not use adjacent selector to achieve that?

#magic:hover + #sibling1 + #sibling2 span {
color: red;
}

Demo (Hover the first element and see the color changes for the 3rd element)

And if you want to change the styles of all the element followed by #magic, than use

#magic:hover + #sibling1 span,
#magic:hover + #sibling1 + #sibling2 span {
/* Styles goes here */
}

Demo 2

CSS Adjacent Selector / Meaning

Select .d inside .c that is adjacent to .b inside .a

Yes, that's right. The way you placed your brackets also makes sense to me. Nest them some more to be clearer:

(((.a) .b) + .c) .d

In this example, only the second p.d element is matched:

<div class="a">
<div class="b">
<p class="d"></p> <!-- [1] -->
</div>
<div class="c">
<p class="d"></p> <!-- [2] -->
</div>
<div class="c">
<p class="d"></p> <!-- [3] -->
</div>
</div>
  1. Not selected

    This p.d element isn't contained in an element with the class c.

  2. Selected

    This p.d element is contained in a .c element. The .c element immediately follows a .b element, and both of these share the .a ancestor element.

  3. Not selected

    This p.d element is contained in a .c element. However, this doesn't immediately follow a .b element; instead it comes after another .c element, so its p.d doesn't satisfy the selector.

    If the general sibling combinator ~ were used instead of the adjacent sibling combinator +, as in

    .a .b ~ .c .d

    Then this p.d would be matched.

What is the operator + precedence in CSS grammar?

All compound selectors and combinators in a sequence are processed from right to left, using each selector group as a step. This answer elaborates. (This may be counter-intuitive when you think in brackets; to make sense of it simply treat the brackets as if the outermost ones came first. Either ordering is fine, though, as long as you remember that combinators aren't associative. See the linked answer for details.)



Related Topics



Leave a reply



Submit