Correct Terms and Words for Sections and Parts of Selectors

Correct terms and words for sections and parts of selectors

What is the correct term for the sections of CSS selectors that are separated by commas?

These are called complex selectors. The entire comma-separated list is known as a selector-list.

Within those sections, what is the term for the parts separated by combinators (spaces, +, >, etc)?

These are known as compound selectors.

So, a selector-list is made up of one or more complex selectors separated by commas, and each complex selector is made up of two main parts: compound selectors, and combinators. It can also optionally contain pseudo-elements.

Compound selectors used to have the rather convoluted name "sequence of simple selectors". Worse still, complex selectors were just called "selectors". Needless to say, I recommend using the new terms as they are much more straightforward, much less cumbersome and completely unambiguous compared to their predecessors.


And since I'm here, here are the rest of the definitions...

  • A simple selector is one fundamental component of selectors. It is any one of the following:

    • Universal selector (*), optionally with a namespace
    • Type selector (a, div, span, ul, li, etc), optionally with a namespace
    • Attribute selector ([att], [att=val], etc), optionally with a namespace
    • Class selector (.class)
    • ID selector (#id)
    • Pseudo-class (:pseudo-class)
  • As answered above, a compound selector (formerly a "sequence of simple selectors") is a chain of simple selectors not separated by a combinator:

    a:not([rel="external"]):hover
  • A combinator is another fundamental component of selectors. It is a symbol or token that separates two compound selectors, establishing in its place a relationship between the two elements represented by the two compound selectors. There are currently four combinators in use today:

    • Descendant combinator:

      article p
    • Child combinator:

      /* 
      * The extra spaces in between are whitespace,
      * and are therefore insignificant.
      */
      ul > li
    • Adjacent sibling combinator:

      header + section
    • General sibling combinator:

      h2 ~ p

    More combinators may be introduced in later specifications.

  • And a complex selector (formerly just "selector") is a complete string made up of compound selectors linked by combinators:

    nav[role="main"] > ul:first-child > li
  • The subject of a complex selector is its last, or only, compound selector, representing the element that will be matched or styled. In the above example, the subject of the selector is li.

  • The term selector has been generalized, so it may now refer to any of the following for the purposes of simplicity and brevity, and which one it's referring to at any given moment should be gleaned from context:

    • Simple selector
    • Compound selector
    • Complex selector
    • Selector-list (e.g. the "selector" component of a style rule)

Some personal notes:

  • The term "key selector" was coined by browser vendors for use with selector implementations, and is not an official term. It is often used to mean "subject of the selector" however, because implementations happen to use the subject of the selector as the key for the purposes of determining matches.

  • The term "pseudo-selector" was coined by authors to mix pseudo-classes and pseudo-elements, and is not an official, or indeed meaningful, term. Although you can find it in some early-stage W3C CSS2/3 drafts, that was probably a mistake. Please don't use this term, as it needlessly creates confusion by attempting to group two completely different concepts into a single umbrella term.

  • Pseudo-elements (::pseudo-element) are not simple selectors, and therefore cannot appear in places where only actual elements may be matched. However, they are still considered selectors for the purposes of CSS parsing, and as stated above currently can appear at the end of any complex selector in a list (i.e. at the end of the last, or only, compound selector of each complex selector).

  • In CSS, a typical style rule (formerly "ruleset") consists of a selector and a declaration block.

  • Namespace prefixes are not selectors in their own right, but they may be applied to type selectors, universal selectors and attribute selectors to match components in a document that are (or are not) namespaced.

  • The specificity of a selector currently only refers to that of a single complex selector. When matching rules, any of the complex selectors in a list that match a given element will be considered for specificity calculations. If more than one complex selector matches an element, the most specific one will be used for calculations.

    Specificity will be a more complicated issue with some level 4 selectors, such as :is() and the enhanced :not(), and the of S notation in the enhanced :nth-child() pseudo.

How this selector is called?

Selectors Level 3 calls it sequence of simple selectors:

A sequence of simple selectors is a chain of simple selectors that are not separated by a combinator.

In this case the simple selectors are the class selectors .One and .Two.

.One.Two is also a selector formed only by that sequence of simple selectors.

A selector is a chain of one or more sequences of simple selectors separated by combinators.

.One.Two is also a group of selectors formed only by that selector.

A comma-separated list of selectors represents the union of all elements selected by each of the individual selectors in the list.

In a sequence of simple selectors, order does not matter:

A selector consisting of a single sequence of simple selectors represents any element satisfying its requirements.

so .Two.One and .One.Two are equivalent.

:has vs :matches - Selectors Level 4

In a nutshell:

  • E:has(rs1, rs2) matches E when a different element F matches any of the selector arguments in relation to E. If you know jQuery's :has() selector, this is exactly the same thing.

  • E:matches(s1, s2) matches E when E itself matches any of the selector arguments. Think of :matches() as the direct opposite of :not(), which matches E if E itself does not match any of the arguments.1 You can also think of :matches() as a pseudo-class version of jQuery's .filter() method.

    This notation is equivalent to concatenating every selector argument with E (provided you can actually concatenate them) such that you have a selector list (E)(s1), (E)(s2). For example, div:matches(.foo, .bar) is equivalent to div.foo, div.bar.

This fundamental difference is demonstrated most straightforwardly with the selectors div:matches(p) and div:has(p):

  • div:has(p) matches any div element that has a p descendant.
    This is very similar to div p, except the former targets the div
    and the latter targets the p.

  • Since a div can never be a p, div:matches(p) will never match
    anything. (Likewise, div:not(p) will match all div elements.)


Here's a more complex example with slightly less absurd selectors:

div:has(.foo, .bar)
div:matches(.foo, .bar)

With the following markup:

<div class="foo"></div> <!-- [1] -->
<div class="bar"></div> <!-- [1] -->

<div class="foo bar"> <!-- [2] -->
<p></p>
</div>

<div> <!-- [3] -->
<p class="foo"></p>
</div>

<div> <!-- [3] -->
<div> <!-- [3] -->
<p class="bar"></p>
</div>
</div>

<div> <!-- [4] -->
<div class="foo"> <!-- [5] -->
<p class="bar"></p>
</div>
</div>

Which elements are matched by which selectors?

  1. Matched by div:matches(.foo, .bar)

    The first div element has the "foo" class, and the second div element has the "bar" class, so each of these satisfies its respective selector argument in the :matches() pseudo-class.

  2. Matched by div:matches(.foo, .bar)

    The third div element has both classes, so it matches both selector arguments.

    A note on specificity: both of these arguments have equal specificity, making the total specificity (0, 1, 1), but when an element matches multiple selector arguments with different specificity values, the spec says that the specificity is that of the most specific argument that is matched.

  3. Matched by div:has(.foo, .bar)

    Each of these div elements has a descendant element (in this case, a p) with a class that matches its respective selector argument in the :has() pseudo-class.

  4. Matched by div:has(.foo, .bar)

    This div element has a div.foo descendant and a p.bar descendant, therefore it satisfies both relative selector arguments.

    A note on specificity: because :has() is not yet in the fast profile and is therefore tentatively excluded from CSS, the concept of specificity does not apply at all. There are plans to include a limited version of this in the fast profile for use in CSS, but there is nothing concrete as yet. Any new developments will be added at an appropriate time.

  5. Matched by div:matches(.foo, .bar) and div:has(.foo, .bar)

    This div element matches both pseudo-classes:

    • it is .foo (as it has the "foo" class), and
    • it has a descendant with the "bar" class.

    This element would also match div:matches(.foo, .bar):has(.foo, .bar), which would be a valid level 4 selector since a compound selector can have any combination of pseudo-classes.

Another difference between :matches() and :has() is that :has() accepts what's known as a relative selector. A relative selector has a scope; selector scoping is an entire topic in its own right, but for the purposes of :has(), the scoping element is always the element you attach the :has() pseudo-class to. But, more importantly, a relative selector can either have an implicit descendant combinator, or begin explicitly with a combinator such as >, + or ~ — this combinator is what links the rest of the relative selector to its scoping element.

For example, while :has() defaults to an ancestor-descendant relationship, you can pass a relative selector that begins with + and it then becomes an adjacent-sibling relationship: ul:has(+ p) matches any ul element that is directly followed by a p (and not necessarily one that contains a p descendant).

As for :matches(), while the overview table says that it accepts a list of compound selectors, AFAIK it hasn't yet been set in stone whether it will take a list of compound selectors or complex selectors, and in which profile (fast or complete). But a compound selector is simply the new name for what Selectors 3 currently calls a sequence of simple selectors and a complex selector is an entire series of compound selectors and combinators. A relative selector is more like a complex selector in that respect. See this answer for a non-exhaustive list of the various terms used in selectors.


1 Yes, that's "arguments" in plural — in Selectors 4, :not() can now accept a list of selectors as opposed to a single simple selector. A much-needed enhancement, but it's also to make it consistent with the other new functional pseudo-classes.

Is the comma ( multiple selector ) a CSS selector combinator?

The WHATWG isn't responsible for any CSS specs. The CSS Working Group is a W3C Working Group. The only CSS specs you'll find are all in w3.org, including Selectors. Other specs may define their own selectors (for example, the :defined pseudo-class appears in the HTML spec), but the selector syntax, and how selectors work in general, is defined by Selectors.

In any case, the Selectors spec is right: there is no such thing as a "multiple selector". That's just a term made up by jQuery as if it were a selector itself, but it's not, it's just a notation used to separate multiple selectors. It's not a "selector" itself, let alone one called a "multiple selector", or a combinator despite what jQuery and MDN claim. The Selectors spec has a very clear and very precise definition of "combinator", one that there's no way the comma could ever fit in, instead living in its own section entirely.

See also: Correct terms and words for sections and parts of selectors

Next-sibling combinator should not work with complex selectors

In the specification you can read:

The next-sibling combinator is made of the "plus sign" (U+002B, +) character that separates two sequences of simple selectors. The elements represented by the two sequences share the same parent in the document tree and the element represented by the first sequence immediately precedes the element represented by the second one.

Nothing is saying that it should only work for "sequences of simple selectors". It's simply saying that the + character separates two sequences of simple selectors and it restricts nothing beyond this so you can have more selectors.

Let's consider the following examples:

.a .b + .b
.b + .b
span.a:not(.b) .b + .b > div

In all of them we have the portion .b + .b which is described by:

the "plus sign" (U+002B, +) character that separates two sequences of simple selectors.


If you read at the start of the specification you will find the generic rule that confirms this:

A selector is a chain of one or more sequences of simple selectors separated by combinators. One pseudo-element may be appended to the last sequence of simple selectors in a selector.

A sequence of simple selectors is a chain of simple selectors that are not separated by a combinator. It always begins with a type selector or a universal selector. No other type selector or universal selector is allowed in the sequence.

A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.

Basically we can have

Seq_simple_selectors + Seq_simple_selectors Seq_simple_selectors > Seq_simple_selectors

Then inside each Seq_simple_selectors we can have something like:

 div.a.b#id::before

If we consider your example .a .b + .b you have 3 sequences and in each sequence only one simple selector.

You second example is also fine and working if you have the correct HTML that goes with:

.b + .a .b {
background: red;
height: 20px;
}
<div class="b"></div>
<div class="a">
<div class="b"></div>
</div>

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.

Combine a:visited with a:after, or chaining pseudo-classes with pseudo-elements

You're supposed to use a:visited:after as you're currently doing. It doesn't work not because of an error in your code, but because the issue lies in the :visited pseudo-class — it doesn't permit the use of pseudo-elements because of privacy concerns.

So basically, you won't be able to apply your checkmark icon to visited links only.

Regarding this:

(i'm also a bit fuzzy on when I should or shouldn't add a space before a pseudo-element, or does it even matter?)

It does matter, because the space represents a descendant combinator:

  1. The selector a:visited a:after represents the :after pseudo-element of an a that is a descendant of another a which is a visited link, which in HTML doesn't quite make sense.

  2. The selector a:visited :after is similar to a:visited a:after, except it represents :after of any kind of descendant of a:visited.

    It can be rewritten as a:visited *:after. See the universal selector in the spec.

By omitting the space, you're applying the pseudo-element directly to the element represented by the selector before it, which in your case is a:visited, not any of its descendants.

Difference between the selectors div + p (plus) and div ~ p (tilde)

Adjacent sibling selectors X + Y

Adjacent sibling selectors have the following syntax: E1 + E2, where
E2 is the subject of the selector. The selector matches if E1 and E2
share the same parent in the document tree and E1 immediately precedes
E2, ignoring non-element nodes (such as text nodes and comments).

ul + p {
color: red;
}

In this example it will select only the
element that is immediately preceded by the former element. In this
case, only the first paragraph after each ul will have red text.