ChatGPT解决这个技术问题 Extra ChatGPT

Why do browsers match CSS selectors from right to left?

CSS Selectors are matched by browser engines from right to left. So they first find the children and then check their parents to see if they match the rest of the parts of the rule.

Why is this? Is it just because the spec says? Does it affect the eventual layout if it was evaluated from left to right?

To me the simplest way to do it would be use the selectors with the least number of elements. So IDs first (as they should only return 1 element). Then maybe classes or an element that has the fewest number of nodes — e.g. there may only be one span on the page so go directly to that node with any rule that references a span.

Here are some links backing up my claims

http://code.google.com/speed/page-speed/docs/rendering.html https://developer.mozilla.org/en/Writing_Efficient_CSS

It sounds like that it is done this way to avoid having to look at all the children of parent (which could be many) rather than all the parents of a child which must be one. Even if the DOM is deep it would only look at one node per level rather than multiple in the RTL matching. Is it easier/faster to evaluate CSS selectors LTR or RTL?

3. No - no matter how you read it, the selector always matches the same set of elements.
For what it's worth, a browser can't assume that your IDs are unique. You could stick the same id="foo" all over your DOM, and a #foo selector would need to match all those nodes. jQuery has the option of saying that $("#foo") will always return just one element, because they're defining their own API with its own rules. But browsers need to implement CSS, and CSS says to match everything in the document with the given ID.
@Quentin In a "nonconformant" (to HTML) document IDs can be non-unique, and in those documents CSS requires matching all elements with that ID. CSS itself places no normative requirements on IDs being unique; the text you cite is informative.
@Boris Zbarsky What jQuery does depends on the code path within jQuery. In some cases, jQuery uses the NodeSelector API (native querySelectorAll). In other cases, Sizzle is used. Sizzle doesn't match multiple IDs but QSA does (AYK). The path taken depends on the selector, the context, and the browser and its version. jQuery's Query API uses what I have termed "Native First, Dual Approach". I wrote an article on that, but it is down. Though you may find here: fortybelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
I'm not even sure anyone but you two can figure out what's going on with this long of a conversation. We do have chat for these extended discussions. Anything you really want to keep around should be put into the question or an answer, especially if it's clarifying information. Stack Overflow does not handle discussion in comments well.

B
Boris Zbarsky

Keep in mind that when a browser is doing selector matching it has one element (the one it's trying to determine style for) and all your rules and their selectors and it needs to find which rules match the element. This is different from the usual jQuery thing, say, where you only have one selector and you need to find all the elements that match that selector.

If you only had one selector and only one element to compare against that selector, then left-to-right makes more sense in some cases. But that's decidedly not the browser's situation. The browser is trying to render Gmail or whatever and has the one <span> it's trying to style and the 10,000+ rules Gmail puts in its stylesheet (I'm not making that number up).

In particular, in the situation the browser is looking at most of the selectors it's considering don't match the element in question. So the problem becomes one of deciding that a selector doesn't match as fast as possible; if that requires a bit of extra work in the cases that do match you still win due to all the work you save in the cases that don't match.

If you start by just matching the rightmost part of the selector against your element, then chances are it won't match and you're done. If it does match, you have to do more work, but only proportional to your tree depth, which is not that big in most cases.

On the other hand, if you start by matching the leftmost part of the selector... what do you match it against? You have to start walking the DOM, looking for nodes that might match it. Just discovering that there's nothing matching that leftmost part might take a while.

So browsers match from the right; it gives an obvious starting point and lets you get rid of most of the candidate selectors very quickly. You can see some data at http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665 (though the notation is confusing), but the upshot is that for Gmail in particular two years ago, for 70% of the (rule, element) pairs you could decide that the rule does not match after just examining the tag/class/id parts of the rightmost selector for the rule. The corresponding number for Mozilla's pageload performance test suite was 72%. So it's really worth trying to get rid of those 2/3 of all rules as fast as you can and then only worry about matching the remaining 1/3.

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.


As a little bonus, it makes more sense to read it RTL than LTR even in English. An example: stackoverflow.com/questions/3851635/css-combinator-precedence/…
Note that RTL matching only applies across combinators. It doesn't drill down to the simple selector level. That is, a browser takes the rightmost compound selector, or sequence of simple selectors and attempts to match it atomically. Then, if there's a match, it follows the combinator leftwards to the next compound selector and checks the element in that position, and so on. There is no evidence that a browser reads each part of a compound selector RTL; in fact, the last paragraph shows precisely otherwise (id checks always come first).
Actually, by the time you're matching selectors, at least in Gecko, the tagname and namespace come first. The id (as well as the tagname and classnames) is considered in a pre-filtering step that eliminates most rules without really trying to match the selectors.
That might help depending on what optimizations the UA is doing, but it won't help the pre-filtering step in Gecko I describe above. There's a second filtering step that works on IDs and classes that's used for descendant combinators only that it might help, though.
@Benito Ciaro: Not to mention specificity problems as well.
p
pilau

Right to left parsing, also called as bottom-up parsing is actually efficient for the browser.

Consider the following:

#menu ul li a { color: #00f; }

The browser first checks for a, then li, then ul, and then #menu.

This is because as the browser is scanning the page it just needs to look at the current element/node and all the previous nodes/elements that it has scanned.

The thing to note is that the browser starts processing moment it gets a complete tag/node and needn't have to wait for the whole page except when it finds a script, in which case it temporarily pauses and completes execution of the script and then goes forward.

If it does the other way round it will be inefficient because the browser found the element it was scanning on the first check, but was then forced to continue looking through the document for all the additional selectors. For this the browser needs to have the entire html and may need to scan the whole page before it starts css painting.

This is contrary to how most libs parse dom. There the dom is constructed and it doesn't need to scan the entire page just find the first element and then go on matching others inside it .


G
Gregory A Beamer

It allows for cascading from the more specific to the less specific. It also allows a short circuit in application. If the more specific rule applies in all aspects that the parent rule applies to, all parent rules are ignored. If there are other bits in the parent, they are applied.

If you went the other way around, you would format according to parent and then overwrite every time the child has something different. In the long run, this is a lot more work than ignoring items in rules that are already taken care of.


That's a separate issue. You do the cascading by sorting the rules by specificity and then matching against them in specificity order. But the question here is why for a given rule you match its selectors in a particular way.