Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ordering simple selectors in a sequence

This is merely a conceptual question, concerning good practice.


The question is simple. Is there any "proper" way to order the parts of a jQuery/CSS selector? For instance, say I have an element that has the following attributes:

<div class="red green blue" id="someDiv" anAttr="something">

For good measure, let's also target it when it is being hovered.


Is there any difference between the following selectors?:

div.red.green.blue#someDiv[anAttr='something']:hover
div#someDiv.red.green.blue[anAttr='something']:hover
div.red#someDiv[anAttr='something']:hover.blue.green
div[anAttr='something'].blue:hover#someDiv.red.green

Meaning, does the order of the targets matter, really? Or can I just put them in any-which order I want?

According to this JSFiddle, they all technically work.

like image 917
Liftoff Avatar asked Dec 18 '14 23:12

Liftoff


3 Answers

5.2 Selector syntax

A simple selector is either a type selector or universal selector followed immediately by zero or more attribute selectors, ID selectors, or pseudo-classes, in any order. The simple selector matches if all of its components match.

Note: the terminology used here in CSS 2.1 is different from what is used in CSS3. For example, a "simple selector" refers to a smaller part of a selector in CSS3 than in CSS 2.1. See the CSS3 Selectors module [CSS3SEL].

A selector is a chain of one or more simple selectors separated by combinators. Combinators are: white space, ">", and "+". White space may appear between a combinator and the simple selectors around it.

The elements of the document tree that match a selector are called subjects of the selector. A selector consisting of a single simple selector matches any element satisfying its requirements. Prepending a simple selector and combinator to a chain imposes additional matching constraints, so the subjects of a selector are always a subset of the elements matching the last simple selector.

One pseudo-element may be appended to the last simple selector in a chain, in which case the style information applies to a subpart of each subject.

From: w3.org

like image 134
emmanuel Avatar answered Nov 18 '22 07:11

emmanuel


The ordering can make a difference to the speed for jQuery selectors. I made a jsPerf test for it, and it turns out that just using the ID is the fastest (not surprisingly), and having the attribute selector first is slowest, by a little bit. The others are pretty similar.

Note that I had to remove the :hover selector for jQuery, as that is purely a CSS thing AFAIK.

like image 2
GregL Avatar answered Nov 18 '22 08:11

GregL


Note that, as the quote in emmanuel's answer states, CSS2.1 has slight terminology differences from the current level 3 standard, so I would consider the CSS2.1 definitions obsolete. The syntax remains pretty much the same, but the terminology is more definite: a simple selector now always refers to any one component in each of what are now known as simple selector sequences, or better yet, compound selectors.

Here is the same quote from the level 3 standard, with just the relevant bits:

4. Selector syntax

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.

Unlike CSS2.1, this spec does not use the phrase "in any order" explicitly, but it's easily implied by the fact that it only states the exception for type/universal selectors. As far as I know the only reason for this exception is that a type selector doesn't have any special symbol, which makes it impossible to distinguish from another simple selector were it to occur anywhere else in a compound selector. A universal selector, despite being represented by an asterisk, really just means "any type of element" — a wildcard type selector, if you will, so it follows the same rule for type selectors.

Also check out the upcoming standard which sees more significant changes (and defines the term "compound selector").

I have a few notes to add:

  • It is probably widely known by now that the native selector engines used by most browsers evaluate selectors in right-to-left order. There is a misconception that this applies down to the simple selector level; it does not. Not strictly, anyway. There are various optimizations accounting for some common cases, such as the presence of ID selectors, the presence of class selectors, the presence of dynamic pseudo-classes, and so on. These optimizations almost always take place regardless of their position in a compound selector. Nobody in their right mind would look at the rightmost simple selector first, when it's really the type or ID selector that matters most, especially when you consider that most authors place the ID somewhere near the start, and the type selector can only appear at the beginning.

    I go into much further detail, with examples, in my answer to this question on the subject of simple selector ordering with regards to selector engines.

  • It is implied that jQuery's selector engine Sizzle follows the same rules, but keep in mind that, like the ones built into layout engines, this is but one implementation of the standard. The entire nature of implementations is that everyone does some things similarly and others quite differently, so performance cannot be qualified on an extremely granular level.

    However, it's pretty much common sense that the fewer simple selectors in a sequence, the less work the selector engine has to do in evaluating the selector. You should only be as specific as you need, and no more than that. For example, you can restrict the context of an ID selector with a class selector or a pseudo-class, but you almost never need to go beyond that.

    It's also well known that jQuery optimizes lone ID selectors, class selectors and type selectors to document.getElementById(), document.getElementsByClassName() and document.getElementsByTagName() respectively. You can qualify them with any other selectors and it will still work, but you lose these optimization paths as a trade-off. Similar optimizations exist in native selector engines, but that is as far as similarities go, ones that matter to authors anyway.

  • Speaking of jQuery, because your question is tagged both jquery-selectors css-selectors, note that jQuery does not support certain dynamic pseudo-classes such as :hover. The basic principle remains the same, though. (As mentioned in a comment, it can detect elements that are currently :hover at the time it is called if passed to document.querySelectorAll(), but for obvious reasons, this is almost never useful.)

So, in short: don't worry about performance when it comes to ordering simple selectors. Any deviations in performance are usually inconsequential, with only the most obvious of exceptions. The syntax doesn't impose any other restrictions except the one that's mentioned above. All you have to do is avoid overqualification, and be consistent.

For example, here is how I order my simple selectors — the syntax allows me to choose any order I like, and this is just what feels right for me:

a#id[attr=val].class1.class2:nth-child(n):link:hover
like image 2
BoltClock Avatar answered Nov 18 '22 08:11

BoltClock