无效的 CSS 选择器导致规则被删除:原因是什么? [英] Invalid CSS selector causes rule to be dropped: What is the rationale?

查看:35
本文介绍了无效的 CSS 选择器导致规则被删除:原因是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

谁能帮我找出CSS中引用的错误处理规则背后的基本原理选择器级别 3 规范.

<块引用>

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

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

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

我有以下规则:

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

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

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

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

解决方案

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

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

<小时>

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

<块引用>

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

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

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

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

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

#sectors >div:not(.alpha, .beta, .gamma) {颜色:#808080;背景颜色:#e9e9e9;不透明度:0.5;}

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

#sectors >div:not(.alpha.beta.伽玛)

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

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

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

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

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

<块引用>

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

通过关于 :last-child 的声明,我假设浏览器首先能够解析单个冒号,后跟任意 ident 作为伪类;实际上,您不能假设实现将知道将 :last-child 正确解析为伪类,或者类似于 :lang():not() 使用函数式表示法,因为函数式伪类直到 CSS2 才出现.

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

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

<小时>

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

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

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

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

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

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

I'm looking more for links to mailing list discussions, etc. rather than speculation.

Can anyone help me find out the rationale behind the quoted error handling rules from the CSS Selectors Level 3 spec.

User agents must observe the rules for handling parsing errors:

  • a simple selector containing an undeclared namespace prefix is invalid
  • a selector containing an invalid simple selector, an invalid combinator or an invalid token is invalid.
  • a group of selectors containing an invalid selector is invalid.

Specifications reusing Selectors must define how to handle parsing errors. (In the case of CSS, the entire rule in which the selector is used is dropped.)

I had the following rule:

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

To compensate for IE8's lack of last-child support, I used a class and a JavaScript shim. However, this didn't work because IE8 complies with the CSS spec on error handling, and discards the entire rule because it doesn't recognise one selector. This can be fixed by separating the two selectors in to individual rules.

Why is this desirable? Why doesn't the spec suggest simply discarding the unrecognised selector, but keeping the rest of the rule?

I'd like to know the rationale, as the rules currently seem counter-intuitive.

解决方案

Why is this desirable? Why doesn't the spec suggest simply discarding the unrecognised selector, but keeping the rest of the rule?

The short answer is because it'd be too difficult for implementations to figure out what exactly constitutes "the rest of the rule" (or "the rest of the selector list" for that matter) without getting it wrong and inadvertently messing up layouts, as well as for consistency in error handling, and forward compatibility with future specifications.


I'll preface my long answer with a link to another answer of mine, on handling of invalid selectors. A comment on that answer points directly to section 4.1.7 of the CSS2.1 spec on dealing with errors in selectors within rule sets, which mentions commas in selectors as an example. I think it sums it up pretty nicely:

CSS 2.1 gives a special meaning to the comma (,) in selectors. However, since it is not known if the comma may acquire other meanings in future updates of CSS, the whole statement should be ignored if there is an error anywhere in the selector, even though the rest of the selector may look reasonable in CSS 2.1.

While the comma itself still means grouping two or more selectors as far as selectors are concerned, it turns out that Selectors 4 introduces new functional pseudo-classes that accept selector groups (or selector lists) as arguments, such as :matches() (it even changes :not() so it accepts a list, making it similar to :matches(), whereas in level 3 it only accepts a single simple selector).

This means that not only will you find comma-separated groups of selectors associated with rules, but you'll start finding them within functional pseudo-classes as well (note that this is within a stylesheet only; outside of CSS, selectors can appear in JavaScript code, used by selector libraries and the native Selectors API).

Although not the only reason by far, this alone is enough to potentially over-complicate a parser's error handling rules with a huge risk of breaking the selector, the rule set, or even the layout. In the event of a parsing error with a comma, the parser will have trouble determining whether this selector group corresponds to an entire rule set, or part of another selector group, and how to handle the rest of the selector and its associated rule set accordingly. Instead of trying to guess, risk guessing wrongly and breaking the rule in some way (e.g. by matching and styling all the wrong elements), the safest bet is to discard the rule and move on.

As an example, consider the following rule, whose selector is valid in level 4 but not in level 3, taken from this question of mine:

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

A naïve parser that doesn't understand Selectors 4 may try to split this into three distinct selectors that share the same declaration block, instead of a single selector with a pseudo-class that accepts a list, based on the commas alone:

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

If it simply discards the first and last selectors which are obviously invalid, leaving the second selector which is valid, should it then try to apply the rule to any elements with class beta? It's clearly not what the author intends to do, so if a browser does that, it's going to do something unexpected to this layout. By discarding the rule with the invalid selector, the layout looks just a little saner, but that's an over-simplified example; rules with layout-altering styles can cause even bigger problems if applied wrongly.

Of course, other ambiguities in selector parsing can occur too, which can lead to the following situations:

  • Not knowing where the complex selector ends
  • Not knowing where the selector list ends
  • Not knowing where the declaration block begins
  • A combination of the above

All of which, again, are most easily resolved by discarding the rule set instead of playing the guessing game.

In the case of seemingly well-formed selectors that are unrecognized, such as :last-child as a pseudo-class in your example, the spec makes no distinction between unrecognized selectors and selectors that are just plain malformed. Both result in a parsing error. From the same section that you link to:

Invalidity is caused by a parsing error, e.g. an unrecognized token or a token which is not allowed at the current parsing point.

And by making that statement about :last-child I'm assuming the browser is able to parse a single colon followed by an arbitrary ident as a pseudo-class in the first place; in reality you can't assume that an implementation will know to parse :last-child as a pseudo-class correctly, or something like :lang() or :not() with a functional notation since functional pseudo-classes didn't appear until CSS2.

Selectors defines a specific set of known pseudo-classes and pseudo-elements, the names of which are most likely hardcoded in every implementation. The most naïve of parsers have the entire notation for each pseudo-class and pseudo-element, including the single/double colon(s), hardcoded (I wouldn't be surprised if the major browsers actually do this with :before, :after, :first-letter and :first-line as a special case). So what may seem like a pseudo-class to one implementation might very well be gobbledygook to another.

Since there are so many ways for implementations to fail, the spec makes no distinction, making error handling much more predictable. If a selector is unrecognized, no matter whether it's because it's unsupported or malformed, the rule is discarded. Simple, straightforward, and easy enough to get your head around.


All that said, there is at least one discussion in the www-style public mailing list suggesting that the specification be changed because it may not be so difficult to implement error handling by splitting selectors after all.

I should also mention that some layout engines behave differently, such as WebKit ignoring non-WebKit-prefixed selectors in a rule, applying its own prefixes, while other browsers ignore the rule completely (you can find more examples on Stack Overflow; here's a slightly different one). In a way you could say WebKit is skirting the rule as it is, although it does try to parse comma-separated selector groups smartly in spite of those prefixed selectors.

I don't think the working group has a compelling reason to change this behavior yet. In fact, if anything, they have a compelling reason not to change it, and that's because sites have been relying on this behavior for many years. In the past, we had selector hacks for filtering older versions of IE; today, we have prefixed selectors for filtering other browsers. These hacks all rely on the same behavior of certain browsers discarding rules they don't recognize, with other browsers applying them if they think they're correct, e.g. by recognizing prefixes (or throwing only the unrecognized ones out, as WebKit does). Sites could break in newer versions of those browsers if this rule were to change, which absolutely cannot happen in such a diversified (read: fragmented) Web as ours.

As of April 2013, it was decided in a telecon that this behavior remain unchanged for the reason I've postulated above:

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

Media query-style invalidation refers to invalid media queries in a comma-separated list not breaking the entire @media rule.

这篇关于无效的 CSS 选择器导致规则被删除:原因是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆