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


560

浏览器引擎从右到左匹配CSS选择器。因此,他们首先找到孩子,然后检查父母,看他们是否符合规则的其余部分。

  1. 为什么是这样?
  2. 只是因为规格说明了吗?
  3. 如果从左到右进行评估,是否会影响最终的布局?

对我来说,最简单的方法是使用元素数量最少的选择器。因此,ID优先(因为它们只能返回1个元素)。然后,可能是类或具有最少节点数的元素-例如,页面上可能只有一个范围,因此请使用引用范围的任何规则直接转到该节点。

这是一些支持我的主张的链接

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

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


5
3.不-无论您如何阅读,选择器始终匹配同一组元素。
森那维达斯

39
就其价值而言,浏览器无法假定您的ID是唯一的。您可以在整个DOM上粘贴相同的id =“ foo”,并且#foo选择器需要匹配所有这些节点。jQuery可以选择说$(“#foo”)将始终只返回一个元素,因为它们使用自己的规则定义自己的API。但是浏览器需要实现CSS,CSS表示要使用给定ID匹配文档中的所有内容。
鲍里斯·兹巴斯基

4
@Quentin在“不一致”(对于HTML)中,文档ID可能是唯一的,并且在这些文档中,CSS要求将所有与该ID匹配的元素匹配。CSS本身对ID的唯一性没有任何规范要求;您引用的文字内容丰富。
鲍里斯·扎巴斯基

5
@Boris Zbarsky jQuery的作用取决于jQuery中的代码路径。在某些情况下,jQuery使用NodeSelector API(本机querySelectorAll)。在其他情况下,使用Sizzle。Sizzle不匹配多个ID,但QSA匹配(AYK)。所采用的路径取决于选择器,上下文以及浏览器及其版本。jQuery的Query API使用了我所谓的“本机优先,双重方法”。我写了一篇关于它的文章,但是它失败了。虽然您可能会在这里找到:fortybelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
Garrett

5
我什至不确定任何人,但是你们两个可以弄清楚这段漫长的对话中发生了什么。对于这些扩展讨论,我们确实有闲聊。您真正想保留的所有内容都应放在问题或答案中,尤其是在澄清信息时。堆栈溢出不能很好地处理注释中的讨论。
乔治·斯托克

Answers:


824

请记住,当浏览器进行选择器匹配时,它具有一个元素(它正在尝试确定其样式的元素)以及所有规则及其选择器,并且它需要查找与该元素匹配的规则。这与通常的jQuery不同,例如,您只有一个选择器,并且需要查找与该选择器匹配的所有元素。

如果只有一个选择器,并且只有一个要与该选择器进行比较的元素,那么从左到右在某些情况下更有意义。但这绝对不是浏览器的情况。该浏览器正在尝试呈现Gmail或其他内容,并且正在尝试呈现<span>其样式,并且Gmail在其样式表中添加了10,000多个规则(我没有增加这个数字)。

特别是在这种情况下,浏览器正在查看考虑的大多数选择器,它们与所讨论的元素匹配。因此,问题就成为了决定选择器与匹配项的匹配程度是否尽可能快的问题之一。如果在不匹配的案例中需要做一些额外的工作,则由于在不匹配的案例中保存的所有工作,您仍然会获胜。

如果仅通过将选择器的最右边部分与您的元素进行匹配而开始,那么它可能会不匹配并且您已经完成了。如果匹配,则您需要做更多的工作,但只与树的深度成比例,在大多数情况下,树的深度并不大。

另一方面,如果您从匹配选择器的最左边开始,您将如何匹配呢?您必须开始遍历DOM,寻找可能与之匹配的节点。只是发现最左边部分没有匹配项可能需要一段时间。

因此,浏览器从右边匹配;它提供了一个明显的起点,并使您可以很快地摆脱大多数候选选择器。您可以在http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665上看到一些数据(尽管表示法令人困惑),但最终结果是特别适用于Gmail两年前,对于70%的(规则,元素)对,您只需检查规则最右边选择器的标记/类/标识部分,就可以确定该规则不匹配。Mozilla的页面加载性能测试套件的相应数字是72%。因此,真的有必要尝试尽快消除所有规则的2/3,然后再担心匹配其余1/3。

还要注意,浏览器已经做过其他优化,甚至可以避免匹配绝对不匹配的规则。例如,如果最右边的选择器具有一个ID,并且该ID与元素的ID不匹配,则在壁虎中根本不会尝试将该选择器与该元素进行匹配:尝试的“具有ID的选择器”的集合来自对元素ID的哈希表查找。因此,仅考虑最右边选择器的标记/类/ ID,这就是70%的规则很有可能匹配,但仍然不匹配。


5
作为一点好处,即使是英文,也要比RTR读RTL更有意义。一个例子:stackoverflow.com/questions/3851635/css-combinator-precedence/...
BoltClock

6
请注意,RTL匹配仅适用于组合器。它不会深入到简单的选择器级别。也就是说,浏览器采用最右边的复合选择器简单选择器的序列,并尝试进行原子匹配。然后,如果存在匹配项,它将跟随组合器向左移动到下一个复合选择器,并检查该位置的元素,依此类推。没有证据表明浏览器会读取复合选择器RTL的每个部分。实际上,最后一段恰好相反(id检查始终排在最前面)。
BoltClock

5
实际上,当您匹配选择器时,至少在Gecko中,标记名和名称空间才是第一位的。在预过滤步骤中考虑了ID(以及标记名和类名),该步骤消除了大多数规则,而没有真正尝试匹配选择器。
鲍里斯·扎巴尔斯基

可能会有所帮助,具体取决于UA正在进行的优化,但对上面我描述的Gecko中的预过滤步骤没有帮助。不过,还有第二个过滤步骤适用于ID和类,该过滤步骤仅在可能有用的情况下用于后代组合器。
鲍里斯·扎巴尔斯基

@Benito Ciaro:更不用说特异性问题了。
BoltClock

33

从右到左的解析(也称为自底向上解析)对于浏览器实际上是有效的。

考虑以下:

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

该浏览器首先检查a,然后li,然后ul,然后#menu

这是因为在浏览器扫描页面时,它只需要查看当前元素/节点以及已扫描的所有先前节点/元素。

需要注意的是,浏览器会在获得完整的标记/节点后立即开始处理,无需等待整个页面,除非找到脚本,在这种情况下,浏览器会暂时暂停并完成脚本的执行,然后前进。

如果相反,它将是低效率的,因为浏览器在第一次检查时就发现了它正在扫描的元素,但随后被迫继续在文档中查找所有其他选择器。为此,浏览器需要具有整个h​​tml,并且可能需要在扫描CSS之前扫描整个页面。

这与大多数库解析dom的方式相反。在那里dom被构建了,它不需要扫描整个页面,只需找到第一个元素然后继续匹配其中的其他元素。


20

它允许从较具体到较不具体的级联。它还允许应用中发生短路。如果更具体的规则适用于父规则适用的所有方面,则所有父规则都将被忽略。如果父级中还有其他位,则会应用它们。

如果您采用其他方法,则将根据父项进行格式化,然后在子项每次有所不同时覆盖。从长远来看,这比忽略已经处理的规则中的项目要多得多。


11
那是一个单独的问题。您可以通过按特定性对规则进行排序,然后按特定性对它们进行匹配来进行级联。但是这里的问题是为什么对于给定规则,您以特定方式匹配其选择器。
鲍里斯·兹巴斯基
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.