9

我一直相信(尽管我现在怀疑这些信念的有效性):

div.name

比:

.name

但是我最近读到大多数 CSS 选择器引擎从右到左读取,在这种情况下,第一个示例实际上不会更慢吗?因为选择器引擎会简单地找到具有类名称的每个元素,然后必须识别其中哪些是divs?

CSS 选择器引擎通常以哪种方式读取?从左到右还是从右到左?如果他们通常从右到左阅读,有人可以向我解释为什么(我看不出从选择器引擎的角度从右到左阅读有什么意义)?

4

2 回答 2

11

但是我最近读到大多数 CSS 选择器引擎从右到左读取,在这种情况下,第一个示例实际上不会更慢吗?

CSS 选择器引擎一般以哪种方式读取?从左到右还是从右到左?如果他们通常从右到左阅读,有人可以向我解释为什么(我看不出从选择器引擎的角度从右到左阅读有什么意义)?

坦率地说,几乎不可能在给定的浏览器中判断哪个选择器会更慢,更不用说跨浏览器了。性能往往会波动和不可预测,尤其是在这种微观尺度和不可预测的文档结构下。即使我们谈论理论性能,它最终还是取决于实现。

话虽如此,如Boris Zbarsky 对 this other question的回答和Guffa 对 yours 的回答所示,典型的浏览器(目前所有主要布局引擎都是如此)获取一个元素并评估所有候选选择器以查看它匹配的,而不是找到与给定选择器匹配的一组元素。这是一个微妙但非常重要的区别。Boris 提供的技术解释不仅非常详细,而且具有权威性(因为他在 Gecko 上工作,Firefox 使用的引擎),所以我强烈建议阅读它。

但我认为我应该解决您问题中似乎另一个问题:

因为选择器引擎会简单地找到具有类名称的每个元素,然后必须识别其中哪些是divs?

以及Patrick McElhaney 的评论

链接的问题解释了为什么选择器通常是从右到左读取的,所以#foo ul.round.fancy li.current是 read li.current, ul.round.fancy, #foo,但它真的在每个元素中从右到左读取吗(.current, li, .fancy, .round, ul, #foo)?应该是吗?

我从来没有实现过 CSS,也没有看到其他浏览器是如何实现它的。我们从上面链接的答案中确实知道,浏览器使用从右到左的匹配来遍历选择器中的>组合器,例如本示例中的组合器:

section > div.second > div.third

如果一个元素不是 a div.third,那么检查它的父元素是否是 adiv.second而其父元素是 a 是没有意义的section

但是,我不相信这种从右到左的顺序会一直深入到简单的选择器级别。换句话说,我不相信浏览器在一系列复合选择器的从右到左评估中对简单选择器序列(也称为复合选择器)的每个部分使用从右到左评估组合器。

例如,考虑这个人为且高度夸张的选择器:

div.name[data-foo="bar"]:nth-child(5):hover::after

现在,不能保证浏览器一定会以下顺序检查元素的这些条件:

  1. 指针是否在该元素上?
  2. 该元素是其父元素的第 5 个子元素吗?
  3. 此元素是否具有data-foo值为 的属性bar
  4. 这个元素有一个name类吗?
  5. 这是一个div元素吗?

除了简单的选择器杂乱无章之外,这个选择器在功能上与上述相同,也不必按以下顺序进行评估:

div:hover[data-foo="bar"].name:nth-child(5)::after
  1. 该元素是其父元素的第 5 个子元素吗?
  2. 这个元素有一个name类吗?
  3. 此元素是否具有data-foo值为 的属性bar
  4. 指针是否在该元素上?
  5. 这是一个div元素吗?

根本没有理由出于绩效原因执行这样的命令。事实上,我认为首先选择某些类型的简单选择器可以提高性能,无论它们在序列中的哪个位置。(您还会注意到::after没有考虑 ——这是因为伪元素不是简单的选择器,甚至从未进入匹配方程。)

例如,众所周知,ID 选择器是最快的。好吧,鲍里斯在他对链接问题的回答的最后一段中说:

另请注意,浏览器已经进行了其他优化,以避免尝试匹配绝对不匹配的规则。例如,如果最右边的选择器有一个 id 并且该 id 与元素的 id 不匹配,那么在 Gecko 中将不会尝试将该选择器与该元素匹配:尝试的“具有 ID 的选择器”集合来自对元素 ID 的哈希表查找。所以这是 70% 的规则,在考虑到最右边选择器的标签/类/id 之后,它们有很大的匹配机会仍然不匹配。

换句话说,你是否有一个看起来像这样的选择器:

div#foo.bar:first-child

或这个:

div.bar:first-child#foo

Gecko 将始终首先检查 ID 和类,而不管它在序列中的位置。如果元素没有与选择器匹配的 ID 和类,那么它会立即被丢弃。如果你问我,那就太快了。

这只是 Gecko 的一个例子。这在实现之间也可能有所不同(例如 Gecko 和 WebKit 可能与 Trident 甚至 Presto 不同)。当然,有一些供应商普遍同意的策略和方法(首先检查 ID 可能没有区别),但小细节可能会有所不同。

于 2012-04-11T15:05:22.723 回答
6

选择器引擎不会寻找元素来应用规则,它会寻找适用于特定元素的规则。因此,从右到左阅读选择器是有意义的。

像这样的选择器:

div span.text a.demo

将使选择器引擎执行这些检查以查看选择器是否适用于元素:

  • 它是类的一个a元素demo吗?
  • 它是否有一个span作为类元素的祖先text
  • 该元素是否具有作为元素的祖先div
于 2012-04-11T13:02:44.947 回答