CSS 错误处理
当 CSS 中存在错误(例如无效值或缺少分号)时,浏览器(或其他用户代理)不会像 JavaScript 中那样抛出错误,而是会优雅地恢复。浏览器不会提供与 CSS 相关的警报,也不会以其他方式指示样式中发生了错误。它们只是丢弃无效内容并解析后续的有效样式。这是 CSS 的特性,而不是错误。
本指南讨论了 CSS 解析器 如何丢弃无效 CSS。
CSS 解析器错误
当遇到 CSS 错误时,浏览器的 解析器 会忽略包含错误的行,丢弃最少的 CSS 代码,然后恢复到正常 解析 CSS。 “错误恢复”只是忽略或跳过无效内容。
浏览器忽略无效代码的事实使得能够使用新的 CSS 功能,而无需担心在旧版浏览器中出现任何问题。浏览器可能无法识别新功能,但这没关系。丢弃无效内容而不抛出错误允许新旧语法在同一规则集中共存,但请记住,应按此顺序指定它们。例如
div {
display: inline-flex;
display: inline flex;
}
该 display
属性既接受传统的单值语法,也接受 多关键字语法。浏览器将呈现旧语法,直到它们将新语法识别为有效,此时新语法将覆盖旧语法。如果用户使用的是旧版浏览器,则有效的回退不会被新的 CSS 覆盖,因为浏览器将其视为无效。
浏览器因错误而忽略的 CSS 类型和数量取决于错误的类型。下面列出了一些常见的错误情况
- 对于 At 规则中的错误,忽略(失败)单行还是整个 At 规则取决于 At 规则和错误类型。
- 如果错误是无效的选择器,则整个声明块将被忽略。
- 由于属性声明之间缺少分号而导致的错误会导致无效值,在这种情况下,多个属性值声明将被忽略。
- 如果错误是属性名称或值,例如无法识别的属性名称或无效的数据类型,则属性值声明将被忽略。
- 如果错误是由于缺少结束括号导致的,则忽略的范围取决于浏览器解析嵌套 CSS 错误的能力。
在解析每个声明、样式规则、@规则等之后,浏览器会根据其预期针对该构造的语法检查解析的内容。如果内容与该构造的预期语法不匹配,则浏览器将其视为无效并忽略它。
@规则错误
@
符号,在 CSS 规范中称为 <at-keyword-token>
,表示 CSS at-rule
的开头。一旦@规则以@
符号开头,从解析器的角度来看,任何内容都不会被认为是无效的。直到第一个分号 (;
) 或开花括号 ({
) 之前的所有内容都是@规则的前导部分。每个@规则的内容都根据该特定@规则的语法规则进行解释。
语句@规则,例如@import
和 @namespace
声明,仅包含前导部分。分号立即结束语句@规则的@规则。如果前导部分的内容根据该@规则的语法无效,则@规则将被忽略,浏览器将在遇到下一个分号后继续解析 CSS。例如,如果@import
@规则出现在除@charset
、@layer
或其他@import
语句之外的任何 CSS 声明之后,则@import
声明将被忽略。
@import "assets/fonts.css" layer(fonts);
@namespace svg url(http://www.w3.org/2000/svg);
如果解析器在遇到分号之前遇到花括号 ({
),则@规则将被解析为块@规则。块@规则(如@font-face
和 @keyframes
)包含由花括号 ({}
) 括起来的一组声明。开花括号通知浏览器@规则前导部分在哪里结束以及@规则的主体在哪里开始。解析器向前查找,寻找匹配的块(由()
、{}
或[]
括起来的内容),直到找到一个不与任何其他花括号匹配的闭花括号 (}
):这将关闭@规则的主体。
不同的@规则具有不同的语法规则、不同的(或没有)描述符以及不同的规则来确定什么(如果有)将使整个@规则无效。每个@规则的预期语法以及如何处理错误都记录在相应的@规则页面上。无效内容的处理方式取决于错误。
例如,@font-face
规则需要font-family
和src
描述符。如果省略或无效,则整个@font-face
规则无效。包含不相关的描述符、任何其他具有无效值的有效字体描述符或@font-face
嵌套块中的属性样式声明都不会使字体声明无效。只要包含字体名称和字体源且有效,则@规则中任何无效的 CSS 都会被忽略,但@font-face
块仍将被解析。
虽然@keyframe
@规则的语法与@font-face
规则的语法非常不同,但错误类型仍然会影响忽略的内容。重要声明(用important
标记)和无法进行动画处理的属性在关键帧规则中会被忽略,但它们不会影响在同一关键帧选择器块中声明的其他样式。包含无效的关键帧选择器(例如小于0%
或大于100%
的百分比值,或省略%
的<number>
)会使关键帧选择器列表无效,因此样式块将被忽略。无效的关键帧选择器只会使无效选择器的样式块无效;它不会使整个@keyframe
声明无效。另一方面,在两个关键帧选择器块之间包含样式将使整个@keyframe
@规则无效。
某些@规则几乎总是有效的。@layer
@规则以常规形式和嵌套形式出现。@layer
语句语法仅包含前导部分,以分号结尾。或者,嵌套语法在prelude之后具有在花括号之间嵌套的层样式。省略闭花括号可能是逻辑错误,但不是语法错误。在@layer
中缺少闭花括号的情况下,闭花括号应该出现之后的所有样式都被解析为在@规则prelude中定义的级联层中。CSS 有效,因为没有语法错误;没有任何内容被丢弃。语法错误可能会导致命名或匿名层为空,但层仍将创建。
选择器列表中的错误
编写选择器时可能会犯很多错误,但只有无效的选择器会导致选择器列表无效(参见无效选择器列表)。
如果您为不存在的类、id 或元素(或自定义元素)包含class
、id
或type
选择器,这可能是逻辑错误,但不是语法错误。但是,如果您在伪类或伪元素中出现错别字,则可能会创建无效的选择器,这是解析器需要解决的错误。
如果选择器列表包含任何无效的选择器,则整个样式块将被忽略。有一些例外:如果无效的选择器位于:is
或:where
伪类(接受宽容选择器列表)内,或者未知的选择器是-webkit-
前缀的伪元素,则仅忽略未知的选择器,因为它不匹配任何内容。选择器列表不会失效。
在这些例外情况之外,选择器列表中单个无效或不受支持的选择器将使整个规则无效,并且整个选择器块将被忽略。然后浏览器将查找闭花括号并从该点继续解析。
-webkit-
异常
由于选择器和属性名称(和值)中过度使用浏览器特定前缀而导致的遗留问题,浏览器通过将所有以不区分大小写的-webkit-
前缀开头且不以()
结尾的伪元素视为有效来避免过度使选择器列表无效。
这意味着像::-webkit-works-only-in-samsung
这样的伪元素不会使选择器列表无效,无论代码在哪个浏览器中运行。在这种情况下,伪元素可能无法被浏览器识别或支持,但它不会导致整个选择器列表及其关联的样式块被忽略。另一方面,带有函数表示法的未知前缀选择器::-webkit-imaginary-function()
将使整个选择器列表无效,浏览器将忽略整个选择器块。
CSS 声明块中的错误
在声明块中的 CSS 属性和值方面,如果属性或值无效,则该属性值对将被忽略和丢弃。当用户代理解析或解释声明列表时,任何点的未知语法都会导致浏览器的解析器仅丢弃当前声明。然后它在遇到下一个分号或闭花括号后继续解析 CSS,以先出现的为准。
此示例包含错误。解析器忽略错误(以及注释),向前查找直到遇到分号,然后重新开始解析
p {
/* Invalid syntax due to missing semi-colon */
border-color: red
background-color: green;
/* Valid syntax but likely a logic error */
border-width: 100vh;
}
此选择器块中第一个声明无效的原因是缺少分号,并且该声明不是选择器块中的最后一个声明。缺少分号的属性会被忽略,其后的属性值对也会被忽略,因为浏览器只在遇到分号或闭括号后才会继续解析。具体来说,border-color
的值被解析为red background-color: green;
,这不是有效的<color>
值。
border-width
值为100vh
可能是错误,但不是错误。由于它在语法上有效,因此它将被解析并应用于与选择器匹配的元素。
供应商前缀
当浏览器不理解供应商前缀的属性名称和属性值时,将被视为无效并忽略。仅忽略包含无效属性或值的单个规则。解析器查找下一个分号或闭花括号,然后从那里继续解析。
您可能会遇到如下所示的旧版 CSS
/* Prefixed values */
.wrapper {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
display: block flex;
}
/* Prefixed properties */
.rounded {
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
-ms-border-radius: 50%;
-o-border-radius: 50%;
border-radius: 50%;
}
在此示例中,每个块中的最后一个声明在所有浏览器中都有效 - display: flex;
和 border-radius: 50%;
。由于级联出现顺序规则,浏览器将应用它们理解的任何前缀声明,然后使用标准的非前缀版本覆盖这些值。
注意:尽可能避免包含前缀属性或属性值。如果必须使用它们,请如上所示在非前缀版本之前声明前缀版本。
自动关闭结尾的错误
如果样式表在规则、声明、函数、字符串或注释仍处于打开状态时结束,则解析器将自动关闭所有未关闭的内容。
如果最后一个分号和样式表末尾之间的内容有效,即使不完整,CSS 也会正常解析。例如,如果您在关闭<style>
之前未能关闭@keyframe
声明,则动画仍然有效。
<style>
@keyframes move {
100% {
transform: translatex(100vw)
</style>
这里move
动画有效。未能正确关闭 CSS 语句并不一定会使语句无效。也就是说,不要利用 CSS 的宽容性。始终关闭所有语句和样式块。这使您的 CSS 更易于阅读和维护,并确保浏览器按照您的意图解析 CSS。
未关闭的注释
未关闭的注释是逻辑错误,而不是语法错误。如果注释以/*
开头但未关闭,则所有 CSS 代码(直到后续注释中的结束分隔符 (*/
) 或样式表末尾,以先出现的为准)都是注释的一部分。虽然未关闭的注释不会使您的 CSS 无效,但它会导致在开头分隔符 (/*
) 之后出现的 CSS 被忽略。
<style>
/* this comment is not closed
@keyframes move {
0% {transform: translatex(0);}
100% {transform: translatex(100vw);}
}
</style>
<p style="/* another unclosed comment">Parsed as HTML.</p>
在此示例中,两个 CSS 注释都没有关闭,但结束的</style>
标签关闭了第一个注释,而style
属性的结束引号关闭了第二个注释。
语法检查
在解析每个声明、样式规则、@规则等之后,用户代理会检查语法是否遵循该声明的规则。例如,如果属性值的数据类型错误或描述符对于所描述的@规则无效,则不匹配预期语法的內容将被视为无效并被忽略。
每个 CSS 属性都接受特定的数据类型。例如,background-color
属性接受有效的<color>
或 CSS 全局关键字。当分配给属性的值类型错误时,例如background-color: 45deg
,则声明无效,因此被忽略。
无效的自定义属性
自定义属性在声明时通常被认为是有效的,但在访问时可能会创建无效的 CSS,即它们可能用作(通过 var()
函数)不支持该值类型的属性的值。浏览器在遇到每个自定义属性时都会对其进行解析,而不管属性在何处被使用。
通常,当属性值无效时,声明会被忽略,并且该属性回退到最后一个有效值。但是,无效的计算自定义属性值的工作方式略有不同。
当 var()
替换无效时,声明不会被忽略,并且会使用该属性的 初始 值或 继承 值。属性被设置为一个新值,但可能不是预期值。
让我们看一个例子来说明这种行为
:root {
--theme-color: 45deg;
}
body {
background-color: var(--theme-color);
}
在上面的代码中,自定义属性声明是有效的。background-color
声明在计算时也是有效的。但是,当浏览器将自定义属性中的 var(--theme-color)
替换为 45deg
作为 background-color
属性的值时,语法无效。<angle>
不是有效的 background-color
值。在这种情况下,声明不会被视为无效而忽略。相反,当自定义属性类型错误时,如果属性是可继承的,则该值将从其父级继承。如果属性不可继承,则使用默认的初始值。对于 background-color
,属性值不是继承值,因此使用 transparent
的初始值。
为了更好地控制自定义属性的回退方式,可以使用 @property
at-rule 来定义属性的初始值。
@property --theme-color {
syntax: "<color>";
inherits: false;
initial-value: rebeccapurple;
}