CSS 属性值处理

对于文档树中的每个元素,浏览器都会为适用于该元素的每个 CSS 属性赋予一个值。给定元素或盒子的每个 CSS 属性的渲染值是基于样式表定义、继承、层叠、依赖关系、单位转换和显示环境计算得出的结果。本指南通过探讨指定值、计算值、使用值和实际值等关键概念,概述了用于定义每个 CSS 最终如何渲染的处理步骤。

属性值

应用于元素或伪元素的每个样式都基于单个 CSS 属性声明。每个 CSS 属性只有一个值。应用的值由适用于该元素或伪元素的所有属性声明的层叠值决定,而应用的单个值来自于根据层叠算法层叠排序中排名最高的属性声明。

当有多个声明值时(即有多个声明为同一元素提供相同或不同的属性值),每个属性值仍然必须来自单个属性名-值对,因为每个属性只应用一个值,即使该值是逗号分隔的值列表。

为了确定应用哪个声明值,用户代理会收集并处理来自不同来源的所有样式,例如内联样式、内部和外部样式表。

层叠决定了当多个相互冲突的样式作用于同一元素时应该应用哪个值。层叠算法定义了用户代理如何组合源自不同来源、作用域和/或的属性值。当选择器匹配一个元素时,来自最高优先级来源的属性的声明值将被应用,即使来自较低优先级来源的选择器具有更高的特异性

某些属性会从其父元素继承值,除非被显式覆盖。继承可能在元素上特定属性没有样式信息时发生。如果该属性是可继承的,其值将设置为父元素的计算值。如果该属性不可继承,其值将设置为该元素的初始值

在逐步应用层叠规则和默认值之后,浏览器会确保视觉呈现与处理后的 CSS 相匹配。

处理概述

在深入了解各个值阶段之前,了解值处理中发生的三个主要阶段非常重要:筛选层叠默认

筛选

筛选是识别应用于每个元素的所有声明的过程。一个声明仅在以下情况下才适用于一个元素:

  • 该声明属于当前应用于本文档的样式表
  • 任何包含该声明的条件规则(如 @media@supports)当前为真。
  • 该声明属于其选择器与该元素匹配的样式规则
  • 该声明在语法上是有效的:浏览器能识别该属性名,并且值与该属性的预期语法相匹配

只有有效的声明才会成为声明值。具有无效属性名或无效值的声明会根据 CSS 错误处理规则被过滤掉。

在此示例中,只有 font-sizefont-weight 声明被处理。CSS 解析器会过滤掉错误,忽略或“过滤”掉具有无效属性名的声明。

css
p {
  font-size: 1.25em;
  colr: blue;
  font-weight: bold;
}

筛选完成后,每个元素对于每个 CSS 属性都有零个或多个声明值。这些声明值是层叠处理阶段的起点。

层叠

当多个声明应用于同一元素的同一属性时,层叠会解决这些冲突。层叠使用层叠排序算法对声明进行排序。

例如,两个 font-size 声明都匹配 <p class="large">CSS is fun!</p>,但第二个声明被应用,因为它具有更高的特异性。两个声明都来自开发者样式表,但第二个选择器的特异性为 0-1-1,而第一个为 0-0-1

css
p {
  font-size: 1em;
}

p.large {
  font-size: 1.5em;
}

层叠之后,浏览器会为每个元素上的每个属性确定层叠值。这个值将用于下一个处理阶段:默认

默认

默认确保每个元素上的每个属性都有一个值。这涉及在没有 CSS 声明显式设置该属性值时应用默认属性值。这包括:

默认处理的结果是,每个属性都保证有一个指定值

请注意,显式默认关键字(initialinheritunsetrevertrevert-layer)也会被解析为其对应的值,以确定指定值

处理阶段

作为文档扁平化元素树一部分的所有元素都具有声明值层叠值指定值计算值使用值实际值。对于某个特定属性,这些值可能相同,也可能不同。例如,如果你的大型代码库中包含 CSS p { font-size: 1.25em; },并且你的 HTML 中包含 <p class="large">CSS is fun!</p>,那么这个段落的字号会是多少?font-size 值会经历几个阶段,从 em 指定值变为渲染的 px 值。

值处理的阶段是:

这些值用于确定最终的渲染值

声明值

声明值是来自应用于元素的声明中任何语法有效的值。一个元素对于每个属性可以有零个或多个声明值。这些值来自样式表(开发者、用户或用户代理),并在筛选阶段被识别。

继续我们的例子,其中我们的样式表包含 p { font-size: 1.25em; },而链接到该样式表的文档包含 <p class="large">CSS is fun!</p>。可能还有其他 font-size 声明也可能应用于同一段落。用户代理样式表可能为所有段落设置 font-size: 1em,而另一个开发者声明为具有 "large" 类的元素设置 font-size: 2em

css
/* User agent styles */
p {
  font-size: 1em;
}

/* author styles */
p {
  font-size: 1.25em;
}

.large {
  font-size: 2em;
}

我们的样式表中可能还有许多其他的 font-size 声明,但只有选择器匹配该元素的声明才会成为声明值。在这个例子中,因为我们的 <p> 元素有 class="large",所以这三个声明都是该元素的声明值。

层叠值

层叠值是在层叠中胜出的声明值。每个元素的每个属性最多只有一个层叠值。

在我们的声明值中,开发者样式胜过用户代理样式。在同一来源内,特异性更高的样式胜过特异性更低的样式。在这种情况下,层叠值将是 font-size: 2em,它来自开发者样式表,特异性为 0-1-1

css
font-size: 2em;

如果一个属性没有声明值,那么它就没有层叠值,这意味着该属性的指定值将由默认过程确定。

指定值

指定值默认过程的结果。它保证在每个元素的每个属性上都存在。指定值按以下方式确定:

  1. 如果存在层叠值,则层叠值就是指定值。
  2. 如果没有层叠值,且该属性是可继承的,则指定值是父元素的计算值
  3. 如果没有层叠值,且该属性不可继承,则指定值是该属性的初始值

在我们的例子中,由于我们有一个 2em层叠值,这个值就成为了指定值。

css
font-size: 2em;

对于没有层叠值的属性,默认过程会确定其值。例如,如果未指定 color,由于它是一个可继承属性,color 会从父元素的计算值中继承。如果未指定 margin,则会使用其初始0,因为 margin 不是可继承属性

css
color: inherit;
margin: 0;

初始值

一个属性的初始值是其在规范的定义表中列出的默认值。在默认过程中,初始值在以下情况下使用:

  • 对于可继承属性,初始值仅在没有层叠值时用于没有父元素的根元素
  • 对于不可继承属性,初始值在没有层叠值时用于所有元素

你可以通过使用 initial 关键字显式设置初始值。

注意: 初始值可以在每个 CSS 属性参考页面的正式语法部分找到。例如,font-size 的初始值是 medium。初始值不应与浏览器样式表指定的值混淆。

计算值

属性的计算值是在继承过程中从父元素传递给子元素的值。它是在将相对单位和自定义属性等解析为绝对值之后,但在考虑布局特定信息之前的结果。

计算值由指定值计算得出,步骤如下:

  1. 处理特殊值 inheritinitialrevertrevert-layerunset
  2. 进行必要的计算,以达到属性定义表中“计算值”行所描述的值。

得到属性计算值所需的计算通常涉及将相对值(例如 em 单位或百分比)转换为绝对值。例如,如果一个元素的指定值为 font-size: 16pxpadding-top: 2em,那么 padding-top 的计算值就是 32px(字体大小的两倍)。

然而,对于某些属性(其中百分比是相对于可能需要布局来确定的东西,例如 widthmargin-righttext-indenttop),以百分比指定的指定值会变成以百分比表示的计算值。此外,在 line-height 属性上指定的无单位数字会成为计算值,如规范所述。这些在计算值中保留的相对值会在确定使用值时变为绝对值。

使用值

使用值是在对计算值执行所有计算,并用布局特定的细节(例如,将百分比解析为实际像素值)进行细化后的属性值。

每个 CSS 属性都有一个使用值。尺寸的使用值(例如 widthline-height)以像素为单位。简写属性的使用值(例如 background)与其组件属性(例如 background-colorbackground-size)以及与 positionfloat 的值是一致的。

一个元素的 widthinline-size 的使用值是一个像素值,即使该属性的指定值是用百分比或关键字设置的。

如果我们有三个容器元素,其宽度分别设置为 auto50%inherit

css
#no-width {
  width: auto;
}

#width-50 {
  width: 50%;
}

#width-inherit {
  width: inherit;
}

/* Make results easier to see */
div {
  border: 1px solid red;
  padding: 8px;
}

虽然三个指定值 auto50%inherit 是关键字和 <percentage> 值,但使用 window.getComputedStyle(el)["width"]; 获取的 width 返回的是一个绝对长度 px 值。

更改窗口大小或旋转你的移动设备以改变大小和使用值。

渲染值

渲染的值称为实际值,而通过脚本获取的值称为解析值

实际值

属性的实际值是该属性的使用值在应用了任何必要的近似处理之后的值。它是浏览器实现的最终渲染值,包括对渲染怪癖或限制的调整。例如,一个只能渲染整数像素宽度边框的用户代理可能会将边框的厚度四舍五入到最接近的整数。

计算包括以下步骤:

  1. 首先,根据层叠继承的结果或使用初始值来确定指定值
  2. 接着,根据规范计算出计算值(例如,一个带有 position: absolutespan 元素的计算 display 值会变为 block)。
  3. 然后,计算布局,从而得到使用值
  4. 最后,根据本地环境的限制对使用值进行转换,得到实际值。

解析值

属性的解析值是在应用活动样式表并解析这些值可能包含的任何基本计算之后的值。getComputedStyle() 方法返回一个实时的 CSSStyleDeclaration 对象,其中包含应用于指定元素的所有 CSS 属性的解析值。每个解析值要么是计算值,要么是使用值,具体取决于属性。

历史上,getComputedStyle() 返回的是元素或伪元素的计算值。随着 CSS 的发展,“计算值”的概念也在演变,但为了与已部署的脚本向后兼容,getComputedStyle() 返回的值必须保持不变。这些值就是“解析值”。

对于大多数属性,解析值是计算值,但对于少数旧属性(包括 widthheight),它是使用值。CSSOM 规范提供了每个属性的详细信息。

CSS 2.0 将计算值定义为属性计算的最后一步。CSS 2.1 引入了“使用值”的明确定义。一个元素因此可以明确地继承其父元素的宽度/高度,而父元素的计算值是一个百分比。对于不依赖于布局的 CSS 属性(例如 displayfont-sizeline-height),计算值和使用值是相同的。以下列表包含了确实依赖于布局的 CSS 2.1 属性,因此它们具有不同的计算值和使用值(摘自 CSS 2.1 变更:指定值、计算值和实际值):

另见