26

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

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

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

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

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

我有以下规则:

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

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

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

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

4

1 回答 1

38

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

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


在处理无效选择器时,我将在我的长答案前面加上我的另一个答案的链接。对该答案的评论直接指向CSS2.1 规范中关于处理规则集中选择器中的错误的第 4.1.7 节,其中提到了选择器中的逗号作为示例。我认为它总结得很好:

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

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

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

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

例如,考虑以下规则,其选择器在第 4 级有效但在第 3 级无效,取自我的这个问题

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

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

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

如果它只是丢弃明显无效的第一个和最后一个选择器,留下有效的第二个选择器,那么它是否应该尝试将规则应用于任何具有 class 的元素beta?这显然不是作者打算做的,所以如果浏览器这样做,它会做一些意想不到的事情。通过丢弃带有无效选择器的规则,布局看起来有点理智,但这是一个过于简单的例子;如果应用错误,具有更改布局样式的规则可能会导致更大的问题。

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

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

同样,通过丢弃规则集而不是玩猜谜游戏,所有这些问题都最容易解决。

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

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

并且通过发表关于:last-child我的声明,我假设浏览器首先能够解析一个冒号,后跟一个任意标识作为伪类;实际上,您不能假设实现将知道:last-child正确解析为伪类,或者类似的东西:lang():not()使用功能符号,因为功能伪类直到 CSS2 才出现。

选择器定义了一组特定的已知伪类和伪元素,它们的名称很可能在每个实现中都是硬编码的。最天真的解析器拥有每个伪类和伪元素的完整符号,包括单/双冒号,硬编码(如果主要浏览器实际上使用:before,:after和作为特殊情况)。因此,对于一个实现来说,看起来像是伪类的东西很可能是另一个实现的 gobbledygook。:first-letter:first-line

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


尽管如此,在 www 风格的公共邮件列表中至少有一个讨论建议更改规范,因为毕竟通过拆分选择器来实现错误处理可能并不难。

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

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

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

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

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

于 2012-12-12T02:32:08.503 回答