无效的CSS选择器导致规则被删除:理由是什么?

mic*_*d82 23 css w3c css-selectors

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

任何人都可以帮我找出CSS Selectors Level 3规范中引用的错误处理规则背后的基本原理.

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

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

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

我有以下规则:

#menu li.last, #menu li:last-child {
  ...
}
Run Code Online (Sandbox Code Playgroud)

为了弥补IE8缺乏最后一个孩子的支持,我使用了一个类和一个JavaScript垫片.但是,这不起作用,因为IE8符合错误处理的CSS规范,并丢弃整个规则,因为它无法识别一个选择器.这可以通过将两个选择器分成单独的规则来解决.

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

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

Bol*_*ock 37

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

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


在处理无效选择器时,我将用我的另一个答案链接我的长答案.对该答案的评论直接指向CSS2.1规范中关于处理规则集中选择器中的错误的4.1.7节,其中以选择器中的逗号为例.我认为它总结得非常好:

CSS 2.1为选择器中的逗号(,)赋予了特殊含义.但是,由于不知道逗号在将来的CSS更新中是否可能获得其他含义,因此如果选择器中的任何位置存在错误,则应忽略整个语句,即使选择器的其余部分在CSS 2.1中看起来合理.

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

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

虽然不是迄今为止唯一的原因,但仅此一点就足以使解析器的错误处理规则过于复杂,并且存在破坏选择器,规则集甚至布局的巨大风险.如果使用逗号解析错误,解析器将无法确定此选择器组是对应于整个规则集还是对应于另一个选择器组的一部分,以及如何相应地处理选择器的其余部分及其关联的规则集.而不是试图猜测,冒险错误地猜测并以某种方式破坏规则(例如通过匹配和设置所有错误的元素),最安全的赌注是放弃规则并继续前进.

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

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

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

#sectors > div:not(.alpha
.beta
.gamma)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

在看似格式良好的选择器无法识别的情况下,例如:last-child在您的示例中作为伪类,规范不区分无法识别的选择器和仅仅是格式错误的选择器.两者都会导致解析错误.从您链接到的同一部分:

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

并且通过做出关于:last-child我的假设,我假设浏览器能够解析单个冒号,然后首先解析任意身份作为伪类; 实际上你不能假设一个实现会知道:last-child正确地解析为伪类,或类似:lang():not()用函数表示法,因为函数伪类直到CSS2才出现.

选择器定义一组特定的已知伪类和伪元素,其名称很可能在每个实现中都是硬编码的.最天真的解析器具有每个伪类和伪元素的完整符号,包括单/双冒号,硬编码(如果主要浏览器实际上这样做:before,我不会感到惊讶:after,:first-letter并且:first-line作为特例).因此,对于一个实现来说,似乎是一个伪类很可能对另一个实现很糟糕.

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


总而言之,在www式公共邮件列表中至少有一个讨论建议改变规范,因为通过拆分选择器来实现错误处理可能并不那么困难.

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

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

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

   - RESOLVED: Do not adopt MQ-style invalidation for Selectors
               due to Web-compat concerns.

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