CSS选择器由浏览器引擎从右到左匹配。因此,他们首先找到孩子,然后检查他们的父母,看看他们是否符合规则的其余部分。
为什么是这样?
是因为规范说的吗?
如果从左到右进行评估,它是否会影响最终布局?
对我来说,最简单的方法是使用元素数量最少的选择器。首先是ID(因为它们应该只返回1个元素)。然后可能是类或具有最少节点数的元素 - 例如页面上可能只有一个跨度,因此可以使用引用跨度的任何规则直接转到该节点。
以下是备份我的说法的一些链接
http://code.google.com/speed/page-speed/docs/rendering.html
https://developer.mozilla.org/en/Writing_Efficient_CSS
这听起来像这样做是为了避免必须看到父母的所有孩子(可能是很多孩子),而不是一个孩子的所有父母必须是一个孩子。即使DOM很深,它也只会在每个级别查看一个节点而不是RTL匹配中的多个节点。评估CSS选择器LTR或RTL更容易/更快?
-
3.否 - 无论你如何阅读它,选择器总是匹配同一组元素。
-
您建议的解析方式不会真正影响,因为它需要大量访问DOM。我从左到右解析它,大概是jQuery的选择器也从左到右解析它。
-
@Sime Vidas - 那为什么从右到左完成?
-
@ JCOC611 - 但为什么不浏览引擎?
-
@ JCOC611我认为jQuery的sizzle引擎遍历right-> left并使用了内置优化(比如选择器以ID开头)
-
对于它的价值,浏览器不能假设您的ID是唯一的。您可以在DOM上粘贴相同的id ="foo",并且#foo选择器需要匹配所有这些节点。 jQuery可以选择说$("#foo")总是只返回一个元素,因为他们用自己的规则定义自己的API。但浏览器需要实现CSS,而CSS表示要将文档中的所有内容与给定的ID相匹配。
-
问题似乎是选择器匹配而不是选择器解析。
-
@Boris Zbarsky - HTML和CSS规范都说ID必须是唯一的。匹配所有内容是一个决定,以便可以在错误的文档上执行错误恢复,而不是因为规范这样说。
-
@Quentin:ID选择器上的CSS3规范说ID选择器将"任何元素"与该ID匹配,而不是"元素"。
-
@BoltClock - 它还说"使ID类型的属性特别之处在于,在一致的文档中没有两个这样的属性可以具有相同的值"
-
@Quentin在"不一致"(对HTML)文档中,ID可以是非唯一的,在这些文档中,CSS需要匹配具有该ID的所有元素。 CSS本身没有对ID唯一的规范要求;你引用的文字是提供信息的。
-
@Boris Zbarsky jQuery的作用取决于jQuery中的代码路径。在某些情况下,jQuery使用NodeSelector API(native querySelectorAll)。在其他情况下,使用Sizzle。 Sizzle与多个ID不匹配,但QSA确实(AYK)。所采用的路径取决于选择器,上下文以及浏览器及其版本。 jQuery的Query API使用了我所谓的"Native First,Dual Approach"。我写了一篇关于它的文章,但它已经失败了。虽然你可以在这里找到:fortybelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
-
@BorisZbarsky:在整个规范中没有任何一点 - 在任何版本的规范中 - 它是否提到了尝试将CS??S应用于无效文档的结果。 (事实上??,勘误表中的一个是修复一些格式错误的HTML的注释,而不是利用这个机会添加一个应该仍然应用样式的注释。)至于那个ID是唯一的ID?这是规范性的,除非另有说明,否则规范中的每一个文本都是如此。
-
@cHao就CSS而言,没有"无效文档"这样的东西。 CSS只适用于抽象元素树。 HTML定义了这个树的构造方式,它定义了如何对不符合创作一致性标准的文档执行此操作。因此,将CSS应用于具有重复ID的文档的结果实际上完全由两个规范的组合定义。至于它是规范性的,它肯定是:作为创作要求。重复的ID不会验证,但这不会影响UA应如何处理它们。
-
@cHao这是一个使用DOM(也适用于元素树)而不是CSS的示例。看一下dom.spec.whatwg.org/#dom-document-getelementbyid并注意"第一个元素,按树顺序"的使用。为什么你认为它确实如此?因为如果作者没有创作验证文档,那么ID实际上可以重复,并且DOM必须处理它。
-
DOM确实如此。 CSS没有。将#whatever的样式应用于document.getElementById('whatever')返回的一个元素是正确的。在CSS中明确指出ID是唯一的。你破坏了这个规则,没有关于实现如何表现的规则。
-
@BorisZbarsky因为DOM必须能够处理无效文档,并且它指定了如何处理不应该没有爆炸的情况?
-
@RobertMcKee当然,但同样的考虑适用于CSS。
-
@cHao问题是,CSS工作组成员不同意你,浏览器实现不同意你,网站依赖于浏览器实现的行为:网站实际上常常有重复ID并依赖于设计id以设置所有元素的样式。除了理论上的纯洁之外,如果我可能会问,你的论证的好处是什么?
-
@BorisZbarsky你已经提出了一些不错的观点,但是,当我在规范中看到的唯一的东西不这么说时,我看不出你认为CSS工作组成员如何看待你的方式。一些浏览器实现可以/将为具有相同ID值的多个元素设置相同的风格(不是全部,但至少是前4个),并且站点具有重复ID并依赖于它的情况并不常见。实际上很少见到它,我认为我从未见过依赖它。你能说出3吗?我可以告诉你最常用的javascript库不同意你的看法。
-
对不起,我应该说两个最常用的javascript库(jQuery和prototype.js)只会在被要求按id选择时返回一个元素。原型:jsfiddle.net/CTZT6 jQuery:jsfiddle.net/CTZT6/1
-
当给定一个id选择器时,MooTools也只返回一个元素:jsfiddle.net/CTZT6/2
-
@BorisZbarsky:好处是不必担心任何地方没有指定的行为。 CSS工作组似乎并不同意我们中的任何一方,因为他们没有做出任何一种官方声明 - 除了规范中的官方陈述,ID是独一无二的,当然,这给我带来了更大的压力争论比你的。 :P就地球而言,没有必要采用地球上的浏览器,就CSS而言,依赖于未指明的行为只是糟糕的编码。
-
@BorisZbarsky:好处还在于促进CSS和JS之间保证一致的行为。如果您的ID不是唯一的,那么您无论如何也无法保证会发生什么 - 除非您想在您的页面上打一个"此网站工作..."免责声明。
-
@RobertMcKee我认为他们以我的方式看待事情,因为我和他们交谈过。你也可以:只需发送邮件到[email protected]。至于网站,有许多模板化的东西,当它们应该使用类时,使用id进行样式化......而且,当你使用它的选择器时,jQuery不会这样做,但这并不意味着网站不会这取决于CSS引擎。
-
@cHao如果它是事实上的标准,不要担心"没有任何地方指定"的行为毫无意义。当然,这种行为是指定的:CSS表示匹配具有给定ID的事物,并且文档语言定义了具有ID的含义。 HTML定义哪些东西具有哪些ID。结果是多个元素可以具有相同的ID。你必须真正尝试扭曲规范并严格依赖非规范性部分来得出任何其他结论。
-
@BorisZbarsky:它只是对所有现有规范性文件的字面读取。不需要加捻。它实际上需要更多,比如依赖于未在任何地方记录的行为,以得出未定义行为以某种方式定义的结论。
-
@cHao哦,我看到了核心问题。 Web规范中没有"未定义的行为"(至少不是好的行为)。我们试过了;这是一次糟糕的失败。规范现在定义了所有情况下的行为。如果您看到未定义的行为,要么是规范错误,要么是您遗漏了某些内容。
-
@BorisZbarsky:向我展示定义当UA呈现无效文档时CSS必须如何表现的规范。 CSS规范肯定没有,事实上它不会这样做。
-
@BorisZbarsky:事实上,在这种情况下,我甚至会满足于事实上的标准。告诉我浏览器制造商在这种情况下记录了各自UA的行为。
-
@cHao CSS规范在哪里说什么?它在规范性文本中甚至没有"无效文档"的概念。此外,请注意dev.w3.org/csswg/selectors4/#id-selectors明确说明"在不合格的文档中,多个元素可以匹配单个ID选择器"。
-
@BorisZbarsky:看,现在,文字有点类似于基本上需要在完成的规范中才能说"CSS需要"任何东西。但是,在将其视为一项要求之前,需要更明确地表达它;类似于"在不符合文档中,ID选择器必须匹配其ID属性与选择器中的标识符相同的所有元素。""可能"更像是"可能"而不是"必须",并且很容易想象UA从文档中删除非法ID属性,除非明确要求它们因某些原因保留它们。
-
@cHao我引用的文字内容丰富,就像本文其余部分一样。规范要求没有变化,只是对那些似乎对它所需要的东西感到困惑的人的更清晰的解释。这就是为什么它没有说"必须":这对于提供信息的文本来说是无稽之谈。
-
@BorisZbarsky:那么,在这种情况下,仍然没有描述任何特定行为要求的规范性文本。
-
@cHao当然有。你只是故意忽略它。
-
@BorisZbarsky:然后告诉我。明确表示即使我不能反驳它。所需要的只是与规范性文本的链接,明确说明要求。我现在已经问过六次看到这样的文字了,你还没有制作链接。我自己多次查看了这些规范,并且找不到任何这样的要求的单词 - 我所看到的唯一文本要么明显地避免说明一个,要么明确指出ID必须是唯一的。通常两者。
-
没有规范性文本明确说明这一点,就像没有规范性文本明确说明具有相同类的两个元素由类选择器匹配而没有规范性文本说浏览器在解析CSS时不应该崩溃。相关的规范位是w3.org/TR/css3-selectors/#id-selectors,其中说"ID选择器表示具有与ID选择器中的标识符匹配的标识符的元素实例"。它没有定义如何分辨元素的标识符;这取决于文档语言,所以HTML。
-
@cHao文档语言规范有"id属性指定其元素的唯一标识符(ID)。[DOM]"就是这样。这就是UA实施者关于此事的规范性文本的全部内容。如果你只是实现它而不试图在行之间读取,你会得到每个浏览器都有的行为。
-
@BorisZbarsky:如果你做"在线之间阅读",完全有可能写出符合规范的UA,但仍然没有做你声称(没有证据)规范"要求"的东西。因为根据定义,不存在重复的唯一标识符,所以没有文本说明浏览器与具有它们的文档有什么关系。例如,它可以删除除第一个副本之外的所有副本,并且100%符合HTML5和CSS3标准。它可以保留第一个,也可以是最后一个,或者随机的一个,并与HTML4兼容 - 再次与CSS3兼容。
-
@BorisZbarsky:执行其中任何一项操作的浏览器甚至无法设置多个#whatever,因为永远不会有多个#whatever。就我所见过的每个规格而言,该浏览器仍然100%兼容所有这些浏览器。
-
@cHao在规格之间阅读会让你陷入困境。特别是,在这种情况下,您实际上不会符合规范,因为规范作者假设您不在行之间读取。如果他们不得不假设你做了并且在规范中覆盖了所有可能的行间读数,那么规格就会变得非常笨拙。
-
@cHao无论如何,如果您认为规格需要改进,请随时提交反馈。用于CSS的[email protected],用于HTML的[email protected]。
-
@BorisZbarsky:我认为这是我的工作的一部分,在线之间进行阅读 - 检查我的假设并区分实际需要的内容和当前最常用的机制。是的,完全指定错误行为导致一个非常深的兔子洞,并且一路走下来使规范变得笨拙......但是未指定的任何行为都是未定义的,因此可以解释。正是出于这个原因,IMO HTML5首先应该永远不会陷入困境。 (好吧,这一切都会使有效的HTML无关紧要。)
-
@cHao您对Web规范如何工作的想法并不是它们如何工作,因为这样做会导致巨大的互操作问题。这就是现代网络规范旨在完全指定所有行为的原因。如果你看到一个行为似乎未指定的情况,那么规范是错误的,或者你读错了(它可能仍然是错误的,因为它可能被读错了)。
-
@BorisZbarsky:定义语法和一致性标准的全部意义是说"如果你的东西看起来像这样,实现就会理解它。如果没有,我们就无法保证所需的结果。"第二句是第一句话的价值。对于每个不符合要求的案例,语言规范都不能强制要求行为(除了"考虑内容不是$ LANGUAGE")。这远远超出了它的范围,试图这样做会把它变成UA规范 - 而在HTML的情况下,会使语言本身的有效性完全无关紧要。
-
由于HTML无效而导致的"巨大的互操作问题"是因为该文档不符合其声称使用的语言规则。垃圾进垃圾出。规则不是秘密;重要的事情甚至都不复杂。打破它们是没有理由的,而且有价值的语言规范应该做的最后一件事是支持破坏。
-
@cHao不,定义Web规范的全部目的是确保有互操作。定义语法主要是为了您可以定义清晰的可扩展性点。 CSS规范旨在强制要求所有不符合要求的内容的行为,HTML也是如此,并且已经存在多年。我所谈论的"互操作问题"不是由于无效的HTML而是因为UAs无法正确实现规范要求的CSS问题,因为它们在行之间读取或规范显式调用未定义的东西然后被强制规范无意义的UAs实施和网站假设。
-
@cHao在任何情况下,另一个混淆是CSS规范的主要目的实际上是UA规范,而不是语言规范。另一个尝试过,但失败了。
-
@BorisZbarsky:事实上人们甚至可以在线之间读取意味着规范并未涵盖所有情况。规范作者要么故意避免这样做,要么惨遭失败。 (我假设前者,导致(1)所有可用的证据指向该方向,尽管你对作者的意图归属,并且(2)这些不是白痴。:P)无论哪种方式,字面读取specs留下了一个不明显的错误行为。当你试图弄清楚那些情况下实际需要什么时,文字阅读是唯一的选择。
-
至于定义语法,语义等,如果你是对的,那么这一切都没有实际意义。我可以写标签汤,让它一直可靠地工作。为什么要关心HTML是否真的符合?
-
为什么要关心?如果您关心的是它在视觉UA中的外观,那就没有理由;这就是为什么在实践中几乎没有人关心的原因。如果您希望HTML更易于维护且语义更有意义,并且渲染速度更快,则可能需要关注。
-
我甚至不确定除了你们两个以外的人能够弄清楚这段长时间的谈话会发生什么。我们确实聊了聊这些扩展的讨论。你真正想要保留的任何东西应该被提出问题或答案,特别是如果它是澄清信息。 Stack Overflow不能很好地处理评论中的讨论。
-
我可以,我可以说,正在讨论的大部分内容都不属于SO,而是属于邮件列表。
-
也许我之前应该在这里发表评论,但这一切都非常简单:要么遵守规则,要么冒着跨浏览器不一致的风险。这里的所有都是它的。如果您推断"我不关心规则,因为它看起来像我想要的在浏览器中",那么您还没有在足够的浏览器中进行过测试。如果您的HTML中有重复的ID,则不同的浏览器将返回不同的结果。使用IE,Edge,Chrome,Firefox查看此文档。我休息一下。
请记住,当浏览器正在进行选择器匹配时,它有一个元素(它试图确定样式的元素)以及所有规则及其选择器,它需要找到与元素匹配的规则。这与通常的jQuery不同,比如,你只有一个选择器,你需要找到与该选择器匹配的所有元素。
如果您只有一个选择器并且只有一个元素可以与该选择器进行比较,那么在某些情况下从左到右更有意义。但这绝不是浏览器的情况。浏览器正在尝试呈现Gmail或其他任何内容,并且有一个它??正在尝试设置样式,以及Gmail放入其样式表中的10,000多个规则(我没有提供该数字)。
特别是,在这种情况下,浏览器正在查看它所考虑的大多数选择器与所讨论的元素不匹配。所以问题就变成了决定选择器不尽可能快的问题;如果在匹配的情况下需要一些额外的工作,由于您在不匹配的情况下保存的所有工作,您仍然会赢。
如果你只是将选择器的最右边部分与你的元素相匹配,那么它很可能无法匹配而且你已经完成了。如果它匹配,你必须做更多的工作,但只与你的树深度成比例,这在大多数情况下并不是那么大。
另一方面,如果你从匹配选择器的最左边部分开始......你对它有什么匹配?您必须开始遍历DOM,寻找可能与之匹配的节点。只是发现最左边的部分没有任何匹配可能需要一段时间。
所以浏览器从右边匹配;它提供了一个明显的起点,让您可以非常快速地摆脱大多数候选选择器。你可以在http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665上看到一些数据(虽然这种表示法令人困惑),但结果是特别针对Gmail两年前,对于70%的(规则,元素)对,您可以在检查规则最右侧选择器的tag / class / id部分后判断规则不匹配。 Mozilla的页面加载性能测试套件的相应数量为72%。因此,尽可能快地摆脱所有规则中的2/3是非常值得的,然后只担心匹配剩余的1/3。
另请注意,浏览器已经进行了其他优化,以避免尝试匹配绝对不匹配的规则。例如,如果最右边的选择器有一个id并且id与元素的id不匹配,那么在Gecko中根本不会尝试将该选择器与该元素匹配:尝试使用"具有ID的选择器"的集合来自元素ID的哈希表查找。因此,在考虑最右边的选择器的标签/类/ id之后,这是70%的具有很好匹配机会的规则仍然不匹配。
-
作为一个小小的奖励,即使用英语,读取RTL比使用LTR更有意义。例如:stackoverflow.com/questions/3851635/css-combinator-precedence/…
-
请注意,RTL匹配仅适用于组合器。它不会深入到简单的选择器级别。也就是说,浏览器采用最右边的复合选择器或简单选择器序列,并尝试以原子方式匹配它。然后,如果匹配,它将向左跟随组合子到下一个复合选择器并检查该位置的元素,依此类推。没有证据表明浏览器会读取复合选择器RTL的每个部分;实际上,最后一段恰恰表示了其他段落(ID检查始终排在第一位)。
-
实际上,当你匹配选择器时,至少在Gecko中,标记名和命名空间是第一位的。 id(以及标记名和类名)在预过滤步骤中被考虑,该步骤消除了大多数规则,而没有真正尝试匹配选择器。
-
这可能有助于取决于UA正在做什么优化,但它无助于我上面描述的Gecko中的预过滤步骤。然而,第二个过滤步骤适用于仅用于后代组合器的ID和类,它可能会有所帮助。
-
@Benito Ciaro:更不用说特异性问题了。
-
other optimizations browsers already do - 如果开发人员不处理像五彩纸屑这样的CSS规则,它会有所帮助。
-
@BoltClock感谢您指出这一点,我认为这是一个非常重要的细节需要注意。
-
很好地解释!!请帮我区分$('div#post ul.tags li'); & $('div#post').find('ul.tags').find('li');
从右到左解析,也称为自下而上解析实际上对浏览器有效。
考虑以下:
1
| #menu ul li a { color: #00f; } |
浏览器首先检查a,然后是li,然后是ul,然后是#menu。
这是因为当浏览器正在扫描页面时,它只需要查看当前元素/节点以及它已扫描的所有先前节点/元素。
需要注意的是,浏览器开始处理它获得完整标记/节点的时刻,除了找到脚本之外不需要等待整个页面,在这种情况下它暂时暂停并完成脚本的执行然后前进。
如果反之亦然,那将是低效的,因为浏览器在第一次检查时发现了它正在扫描的元素,但后来被迫继续查看所有其他选择器的文档。为此,浏览器需要拥有整个html,并且可能需要在开始css绘制之前扫描整个页面。
这与大多数lib解析dom的方式相反。在那里构建了dom,它不需要扫描整个页面,只需找到第一个元素,然后继续匹配其中的其他元素。
它允许从更具体到更具体的级联。它还允许应用中的短路。如果更具体的规则适用于父规则适用的所有方面,则忽略所有父规则。如果父级中还有其他位,则应用它们。
如果你反过来,你会根据父母进行格式化,然后每当孩子有不同的东西时覆盖。从长远来看,这比忽略已经处理的规则中的项目要多得多。
-
又名"特异性水平"
-
这是一个单独的问题。您可以通过按特异性对规则进行排序,然后按特定顺序对它们进行匹配来进行级联。但这里的问题是为什么对于给定的规则,您以特定的方式匹配其选择器。