为什么浏览器从右到左匹配CSS选择器?

时间:2022-08-22 12:11:46

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.

CSS选择器由浏览器引擎从右到左进行匹配。所以他们先找到孩子,然后检查他们的父母看他们是否符合规定的其他部分。

  1. Why is this?
  2. 这是为什么呢?
  3. Is it just because the spec says?
  4. 是不是因为说明书上说的?
  5. Does it affect the eventual layout if it was evaluated from left to right?
  6. 如果从左到右进行评估,是否会影响最终的布局?

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.

对我来说,最简单的方法就是使用元素最少的选择器。所以id首先(因为它们应该只返回一个元素)。然后可能是节点数最少的类或元素—例如,页面上可能只有一个span,所以使用引用span的任何规则直接访问该节点。

Here are some links backing up my claims

以下是一些支持我的观点的链接

  1. http://code.google.com/speed/page-speed/docs/rendering.html
  2. http://code.google.com/speed/page-speed/docs/rendering.html
  3. https://developer.mozilla.org/en/Writing_Efficient_CSS
  4. 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?

这听起来似乎是为了避免看到父母的所有孩子(可能很多),而不是一个孩子的所有父母都必须是一个孩子。即使DOM是深的,在RTL匹配中,它也只查看每个级别的一个节点,而不是多个节点。评估CSS选择器LTR或RTL更容易/更快吗?

3 个解决方案

#1


762  

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.

请记住,当浏览器执行选择器匹配时,它有一个元素(它试图为之确定样式的元素)以及所有的规则和它们的选择器,并且它需要找到与元素匹配的规则。这与通常的jQuery不同,例如,您只有一个选择器,并且需要找到与该选择器匹配的所有元素。

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).

如果您只有一个选择器,并且只有一个元素可以与该选择器进行比较,那么在某些情况下,从左到右将更有意义。但这显然不是浏览器的情况。浏览器尝试渲染Gmail或其他任何东西,并且有一个,它尝试着样式化,1万+规则Gmail在它的样式表中放置(我没有把这个数字加起来)。

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.

另一方面,如果你从匹配选择器的最左边开始……你对它有什么看法?您必须开始遍历DOM,寻找可能匹配它的节点。只是发现没有匹配的东西,剩下的部分可能需要一段时间。

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.

所以浏览器从右边匹配;它提供了一个明显的起点,让您可以很快地摆脱大多数候选选择器。你可以看到一些数据在http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665(尽管符号混淆),但结果是,Gmail特别是两年前,70%的(规则,元素)对您可以决定规则不匹配后检查标签/类/ id部分最右边的选择器的规则。Mozilla的pageload性能测试套件相应的数量是72%。所以我们应该尽快摆脱2/3的规则然后只考虑剩下的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.

还要注意,浏览器已经进行了其他优化,以避免尝试匹配绝对不会匹配的规则。例如,如果右边的选择器有一个id,id不匹配元素的id,然后将没有尝试匹配选择器与该元素在壁虎:集未遂的“id选择器”来自一个散列表查找元素的id。这是70%的规定仍有很好的机会的匹配不匹配在考虑最右边的的标签/类/ id选择器。

#2


20  

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.

浏览器首先检查a,然后是li,然后是ul,然后是#菜单。

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.

如果它反过来做,则效率会很低,因为浏览器发现了它在第一次检查时扫描的元素,但随后*继续查看所有其他选择器的文档。为此,浏览器需要有完整的html,可能需要扫描整个页面才能开始css的绘制。

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 .

这与大多数libs解析dom的方式相反。dom是构建的,不需要扫描整个页面就可以找到第一个元素,然后继续匹配它里面的其他元素。

#3


18  

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.

如果你反过来做,你可以根据父元素的格式,然后在每次子元素有不同的时候重写。从长远来看,这比忽略已经处理的规则中的项目要多得多。

#1


762  

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.

请记住,当浏览器执行选择器匹配时,它有一个元素(它试图为之确定样式的元素)以及所有的规则和它们的选择器,并且它需要找到与元素匹配的规则。这与通常的jQuery不同,例如,您只有一个选择器,并且需要找到与该选择器匹配的所有元素。

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).

如果您只有一个选择器,并且只有一个元素可以与该选择器进行比较,那么在某些情况下,从左到右将更有意义。但这显然不是浏览器的情况。浏览器尝试渲染Gmail或其他任何东西,并且有一个,它尝试着样式化,1万+规则Gmail在它的样式表中放置(我没有把这个数字加起来)。

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.

另一方面,如果你从匹配选择器的最左边开始……你对它有什么看法?您必须开始遍历DOM,寻找可能匹配它的节点。只是发现没有匹配的东西,剩下的部分可能需要一段时间。

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.

所以浏览器从右边匹配;它提供了一个明显的起点,让您可以很快地摆脱大多数候选选择器。你可以看到一些数据在http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665(尽管符号混淆),但结果是,Gmail特别是两年前,70%的(规则,元素)对您可以决定规则不匹配后检查标签/类/ id部分最右边的选择器的规则。Mozilla的pageload性能测试套件相应的数量是72%。所以我们应该尽快摆脱2/3的规则然后只考虑剩下的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.

还要注意,浏览器已经进行了其他优化,以避免尝试匹配绝对不会匹配的规则。例如,如果右边的选择器有一个id,id不匹配元素的id,然后将没有尝试匹配选择器与该元素在壁虎:集未遂的“id选择器”来自一个散列表查找元素的id。这是70%的规定仍有很好的机会的匹配不匹配在考虑最右边的的标签/类/ id选择器。

#2


20  

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.

浏览器首先检查a,然后是li,然后是ul,然后是#菜单。

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.

如果它反过来做,则效率会很低,因为浏览器发现了它在第一次检查时扫描的元素,但随后*继续查看所有其他选择器的文档。为此,浏览器需要有完整的html,可能需要扫描整个页面才能开始css的绘制。

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 .

这与大多数libs解析dom的方式相反。dom是构建的,不需要扫描整个页面就可以找到第一个元素,然后继续匹配它里面的其他元素。

#3


18  

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.

如果你反过来做,你可以根据父元素的格式,然后在每次子元素有不同的时候重写。从长远来看,这比忽略已经处理的规则中的项目要多得多。