【问题标题】:Invalid CSS selector causes rule to be dropped: What is the rationale?无效的 CSS 选择器导致规则被删除:原因是什么?
【发布时间】:2012-11-28 19:11:44
【问题描述】:

我更多的是寻找邮件列表讨论等的链接,而不是猜测。

谁能帮我找出 CSS Selectors Level 3 规范中引用的错误处理规则背后的基本原理。

用户代理必须遵守处理解析错误的规则:

  • 包含未声明命名空间前缀的简单选择器无效
  • 包含无效简单选择器、无效组合器或无效标记的选择器无效。
  • 包含无效选择器的一组选择器无效。

重用选择器的规范必须定义如何处理解析错误。 (在 CSS 的情况下,使用选择器的整个规则都被删除了。)

我有以下规则:

#menu li.last, #menu li:last-child {
  ...
}

为了弥补 IE8 对 last-child 支持的不足,我使用了一个类和一个 JavaScript shim。但是,这不起作用,因为 IE8 符合 CSS 错误处理规范,并丢弃整个规则,因为它无法识别一个选择器。这可以通过将两个选择器分成单独的规则来解决。

为什么这是可取的?为什么规范不建议简单地丢弃无法识别的选择器,而是保留规则的其余部分?

我想知道其中的原理,因为目前的规则似乎违反直觉。

【问题讨论】:

    标签: css css-selectors w3c


    【解决方案1】:

    为什么这是可取的?为什么规范不建议简单地丢弃无法识别的选择器,而是保留规则的其余部分?

    简短的回答是因为实现起来很难弄清楚究竟是什么构成了“规则的其余部分”(或“选择器列表的其余部分”)而不会弄错并无意中弄乱布局,以及错误处理的一致性,以及与未来规范的前向兼容性。


    我将在我的长篇回答前加上another answer of mine 的链接,以处理无效选择器。对该答案的评论直接指向section 4.1.7 of the CSS2.1 spec 处理规则集中选择器中的错误,其中提到了选择器中的逗号作为示例。我认为它总结得很好:

    CSS 2.1 为选择器中的逗号 (,) 赋予了特殊含义。但是,由于不知道逗号在未来的 CSS 更新中是否会获得其他含义,因此如果选择器中的任何地方出现错误,则应该忽略整个语句,即使选择器的其余部分在 CSS 2.1 中看起来很合理。

    尽管就选择器而言,逗号本身仍然意味着对两个或多个选择器进行分组,但事实证明,Selectors 4 引入了新的功能性伪类,它们接受选择器组(或选择器列表)作为参数,例如 :matches() (它甚至改变了:not(),所以它接受一个列表,使它类似于:matches(),而在第3级它只接受一个简单的选择器)。

    这意味着您不仅会找到与规则相关联的以逗号分隔的选择器组,而且您还将开始在功能性伪类中找到它们(请注意,这仅在样式表中;在 CSS 之外,选择器可以出现在 JavaScript 代码中,供选择器库和本机 Selectors API 使用。

    虽然到目前为止不是唯一的原因,但仅此一项就足以使解析器的错误处理规则过度复杂化,并有破坏选择器、规则集甚至布局的巨大风险。如果出现带有逗号的解析错误,解析器将无法确定此选择器组是对应于整个规则集还是另一个选择器组的一部分,以及如何相应地处理选择器的其余部分及其关联的规则集.与其尝试猜测、冒险猜测错误并以某种方式违反规则(例如,通过匹配和设置所有错误元素的样式),最安全的选择是放弃规则并继续前进。

    例如,考虑以下规则,其选择器在级别 4 中有效,但在级别 3 中无效,取自 this question of mine

    #sectors > div:not(.alpha, .beta, .gamma) {
        color: #808080;
        background-color: #e9e9e9;
        opacity: 0.5;
    }
    

    不理解 Selectors 4 的幼稚解析器可能会尝试将其拆分为共享同一声明块的三个不同选择器,而不是仅基于逗号的带有接受列表的伪类的单个选择器:

    #sectors > div:not(.alpha
    .beta
    .gamma)
    

    如果它简单地丢弃明显无效的第一个和最后一个选择器,留下有效的第二个选择器,那么它是否应该尝试将规则应用于类beta的任何元素?这显然不是作者打算做的,所以如果浏览器这样做,它会去do something unexpected to this layout。通过使用无效选择器the layout looks just a little saner 丢弃规则,但这是一个过于简单的示例;如果应用不当,带有改变布局样式的规则可能会导致更大的问题。

    当然,选择器解析中的其他歧义也可能出现,这可能导致以下情况:

    • 不知道复杂选择器的结束位置
    • 不知道选择器列表的结束位置
    • 不知道声明块从哪里开始
    • 以上的组合

    再一次,通过放弃规则集而不是玩猜谜游戏,最容易解决所有这些问题。

    对于无法识别的看似格式正确的选择器,例如 :last-child 在您的示例中作为伪类,规范没有区分无法识别的选择器和格式错误的选择器。两者都会导致解析错误。从您链接到的同一部分:

    无效是由解析错误引起的,例如无法识别的令牌或当前解析点不允许的令牌。

    通过做出关于:last-child 的声明,我假设浏览器首先能够解析单个冒号后跟任意标识作为伪类;实际上,您不能假设实现将知道将 :last-child 正确解析为伪类,或者像 :lang():not() 这样的函数符号,因为函数伪类直到 CSS2 才出现。

    选择器定义了一组特定的已知伪类和伪元素,它们的名称很可能在每个实现中都是硬编码的。最天真的解析器对每个伪类和伪元素都有整个符号,包括硬编码的单/双冒号(如果主要浏览器确实这样做,我不会感到惊讶这与:before:after:first-letter:first-line 作为special case)。因此,对于一个实现来说可能看起来像是伪类的东西,对于另一个实现来说很可能是 gobbledygook。

    由于实现失败的方式有很多,因此规范没有区别,使错误处理更加可预测。如果选择器无法识别,无论是因为它不受支持还是格式错误,都会丢弃该规则。简单、直接、容易上手。


    话虽如此,在 www 风格的公共邮件列表中至少有 one discussion 建议更改规范,因为毕竟通过拆分选择器来实现错误处理可能不是那么困难。

    我还应该提到一些布局引擎的行为不同,例如 WebKit 在规则中忽略非 WebKit 前缀的选择器,应用自己的前缀,而其他浏览器则完全忽略该规则(您可以在 Stack Overflow 上找到更多示例;这是a slightly different one)。在某种程度上,你可以说 WebKit 正在绕过规则,尽管它确实尝试巧妙地解析逗号分隔的选择器组,尽管有这些前缀选择器。

    我认为工作组还没有令人信服的理由来改变这种行为。事实上,如果有的话,他们有一个令人信服的理由不改变它,这是因为网站多年来一直依赖这种行为。过去,我们有过滤旧版本 IE 的选择器技巧;今天,我们为过滤其他浏览器添加了前缀选择器。这些黑客攻击都依赖于某些浏览器丢弃他们不认识的规则的相同行为,如果其他浏览器认为它们是正确的,则应用它们,例如通过识别前缀(或者像 WebKit 那样只丢弃无法识别的前缀)。如果这条规则发生变化,网站可能会在这些浏览器的新版本中出现问题,而这在我们这样多样化(阅读:碎片化)的网络中绝对不会发生。

    截至April 2013,由于我在上面假设的原因,在电话会议中决定这种行为保持不变:

    - 已解决:不要对选择器采用 MQ 样式的失效 由于网络兼容性问题。

    媒体查询样式失效是指以逗号分隔的列表中的无效媒体查询破坏了整个@media规则。

    【讨论】:

    • 虽然我同意#sectors > div:not(.alpha, .beta, .gamma) 的例子,但我不明白为什么用户代理不能解析#menu li.last, #menu li:unknown-pseudo-class 之类的东西。在第一种情况下,出现意外字符,从而导致解析错误;在第二种情况下,解析会很好地工作,而 li:unknown-pseudo-class 选择器永远不会匹配,例如喜欢:hover@media print
    • @Tobias:我终于更新了我的答案来解决这个问题。另请参阅此相关问题:What happens when the browser doesn't support a CSS pseudo-class?
    • 我认为有一些非常简单的解决方案可以绕过未知选择器,甚至是复杂的选择器!例如,解析器在查找仅处理 div:not(.alpha, .beta, .gamma) 等情况的选择器时应该找到括号对。即使在最简单的情况下,浏览器也可以忽略第一个未知选择器之后的整个选择器,使用 {...} 模式作为规则块,并将规则应用于前一个选择器。请注意,浏览器当前显然能够检测到 {...} 块,因为它们需要它来跳过整个规则并继续下一条。
    猜你喜欢
    • 2018-02-26
    • 1970-01-01
    • 2019-01-05
    • 2014-10-26
    • 2012-07-05
    • 2012-09-14
    • 2010-12-09
    • 2010-11-29
    • 2019-09-16
    相关资源
    最近更新 更多