【问题标题】:In which direction do selector engines read, exactly?选择器引擎到底在哪个方向读取?
【发布时间】:2012-04-23 18:46:07
【问题描述】:

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

div.name

比:

.name

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

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

【问题讨论】:

  • 好像有人问过这个问题:stackoverflow.com/questions/5797014/…
  • 第一个说“检查所有 DIV 元素中的类名'name'”,第二个说“检查 ALL 元素中的类名'name'”。对我来说很有意义,第一个会更快,不是吗?
  • 链接的问题解释了为什么选择器通常从右到左读取,所以#foo ul.round.fancy li.current 被读取li.current, ul.round.fancy, #foo,但它真的在每个元素中从右到左读取吗(@987654327 @)?应该吗?
  • 选择器在组合器中从右到左读取,但在div.name.name 之间,区别与读取 RTL 无关,因为引擎一次只查看一个复合选择器。我再次更新了我的答案,现在让它变得不那么混乱了。

标签: css css-selectors


【解决方案1】:

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

这样的选择器:

div span.text a.demo

会让选择器引擎做这些检查,看看选择器是否适用于一个元素:

  • 它是一个a 元素与demo 类吗?
  • 它是否有一个 span 元素的祖先,其类为 text
  • 该元素是否有一个 div 元素的祖先?

【讨论】:

  • +1 虽然它并没有真正解决从右到左的顺序是否在简单/复合选择器级别上完成。
  • @BoltClock'saUnicorn:结论是这无关紧要。由于引擎一次只查看一个元素,因此检查元素类型或元素属性之间没有相关的性能差异。
【解决方案2】:

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

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

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

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

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

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

还有Patrick McElhaney's comment:

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

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

section > div.second > div.third

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

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

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

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

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

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

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

div:hover[data-foo="bar"].name:nth-child(5)::after
  1. 此元素是其父元素的第 5 个子元素吗?
  2. 此元素是否有 name 类?
  3. 此元素是否具有值为bardata-foo 属性?
  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 可能没有区别),但小细节可能会有所不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-20
    • 2010-09-06
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-06
    • 2018-02-25
    相关资源
    最近更新 更多