无效的CSSselect器会导致规则被删除:原因是什么?

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

任何人都可以帮我找出CSS Selectors Level 3规范中引用的error handling规则背后的基本原理。

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

  • 包含未声明的名称空间前缀的简单select器无效
  • 包含无效简单select器,无效组合器或无效标记的select器无效。
  • 包含无效select器的一组select器是无效的。

重复使用select器的规范必须定义如何处理parsing错误。 (在CSS的情况下,放弃使用select器的整个规则。)

我有以下规则:

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

为了弥补IE8缺乏最后一个孩子的支持,我使用了一个类和一个JavaScript垫片。 但是,这不起作用,因为IE8符合error handling的CSS规范,并放弃整个规则,因为它不能识别一个select器。 这可以通过将两个select器分为单独的规则来解决。

为什么这是可取的? 为什么规范不提示简单地丢弃无法识别的select器,而是保持规则的其余部分?

我想知道这个理由,因为目前的规则似乎是违反直觉的。

为什么这是可取的? 为什么规范不提示简单地丢弃无法识别的select器,而是保持规则的其余部分?

简短的回答是因为实现太困难了,究竟是什么构成了“剩下的规则”(或者“select器的其余部分”),却又没有弄错和无意间搞乱了布局,以及error handling的一致性以及与未来规范的前向兼容性。


我将通过链接到我的另一个答案 ,处理无效的select器,来为我的长答案做序。 对这个回答的评论直接指向了规则集中select器error handling的CSS2.1规范第4.1.7节 ,其中提到了select器中的逗号作为例子。 我认为它很好地总结:

CSS 2.1给select器中的逗号(,)赋予了特殊的含义。 但是,由于不知道逗号是否可以在将来的CSS更新中获得其他含义,所以如果select器中的任何地方出现错误,则应该忽略整个语句,尽pipe在CSS 2.1中select器的其余部分看起来是合理的。

虽然逗号本身仍然意味着就select器而言将两个或多个select器分组,但事实certificate,select器4引入了接受select器组(或select器列表)的新的函数伪类,例如:matches() (它甚至更改:not()因此它接受一个列表,使其类似于:matches() ,而在级别3中它只接受一个简单的select器)。

这意味着您不仅可以find与规则相关联的逗号分隔的select器组,还可以在function伪类中find它们(请注意,这仅在样式表中;在CSS之外,select器可以出现在JavaScript代码,由select器库和本地select器API使用 )。

虽然不是到目前为止唯一的原因,但仅此一项就足以使parsing器的error handling规则过分复杂化,并且存在破坏select器,规则集甚至布局的巨大风险。 在用逗号分析错误的情况下,parsing器将无法确定该select器组是否对应于整个规则集或另一个select器组的一部分,以及如何相应地处理select器的其余部分及其关联的规则集。 而不是试图猜测,错误地猜测风险和以某种方式破坏规则(比如通过匹配和造型所有错误的元素),最安全的方法就是丢弃规则并继续前进。

作为一个例子,考虑下面这个规则,它的select器在第4级有效,而不是第3级:

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

一个不理解select器4的天真的parsing器可能会试图将它分成三个不同的select器,它们共享相同的声明块,而不是单独的select器,只有一个伪类接受一个列表,而这个select器是基于逗号的:

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

如果只是简单地丢弃显然无效的第一个和最后一个select器,则留下第二个有效的select器,是否应该将规则应用于任何具有beta类的元素? 这显然不是作者想要做的事情,所以如果浏览器这样做的话,那么这个布局就会出乎意料的 。 通过丢弃与无效select器的规则, 布局看起来有点儿安慰 ,但这是一个过分简化的例子; 如果使用错误的话,改变样式的规则会导致更大的问题。

当然,在select器parsing中也会出现其他的歧义,这可能会导致以下情况:

  • 不知道复杂select器的结束位置
  • 不知道select器列表结束的地方
  • 不知道声明块在哪里开始
  • 以上的组合

所有这些都是通过抛弃规则集而不是玩猜谜游戏来解决的。

如果看起来格式良好的select器是无法识别的,例如:last-child在您的示例中将:last-child为伪类,则规范不会区分无法识别的select器和只是普通格式错误的select器。 两者都导致parsing错误。 从您链接到的相同部分:

无效是由parsing错误引起的,例如无法识别的标记或当前parsing点不允许的标记。

通过声明:last-child我假设浏览器能够parsing一个单独的冒号,然后是一个任意的ident作为伪类, 在现实中,你不能假定一个实现将知道parsing:last-child是正确的伪类,还是类似于:lang():not()带有function符号,因为function伪类没有出现直到CSS2。

select器定义了一组特定的已知伪类和伪元素,其名称很可能在每个实现中被硬编码。 最天真的parsing器有每个伪类和伪元素的全部符号,包括单/双冒号,硬编码(如果主要浏览器实际上是这样做的:before:after:first-letter:first-line作为特例的 :first-line )。 所以对于一个实现来说,似乎是一个伪类,可能是对另一个实现的。

由于实现失败的方式太多,因此规范没有任何区别,使error handling更加可预测。 如果select器无法识别,无论是不支持还是格式错误,该规则都会被丢弃。 简单,直接,容易,让你的头。


尽pipe如此,在www风格的公共邮件列表中至less有一个讨论意味着更改规范,因为通过分割select器来实现error handling可能并不那么困难。

我还应该提到一些布局引擎的行为不同,例如WebKit忽略规则中的非WebKit加前缀的select器,应用自己的前缀,而其他浏览器完全忽略该规则(您可以在Stack Overflow上find更多示例; 稍后不同的 )。 在某种程度上,你可以说WebKit正在避免这个规则,虽然它试图分离逗号分隔的select器组,尽pipe有那些前缀select器。

我不认为工作组有一个令人信服的理由来改变这种行为。 事实上,如果有的话,他们有一个令人信服的理由不去改变 ,这是因为网站多年来一直依靠这种行为。 在过去,我们select了黑客过滤旧版本的IE; 今天,我们已经有前缀select器来过滤其他浏览器。 这些黑客都依赖于某些浏览器放弃他们无法识别的规则的相同行为,其他浏览器如果认为他们是正确的,例如通过识别前缀(或者仅仅抛出无法识别的东西,如WebKit那样)来应用它们。 如果这个规则发生变化的话,网站可能会打破这些浏览器的新版本,在我们这样一个多元化的(阅读:碎片化的)networking中绝对不可能发生。

截至2013年4月 ,在一个电话会议上决定,这个行为保持不变,因为我上面所说的原因:

    - 已解决:不要对select器采用MQ风格的失效
               由于网站compat关心。

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