跳到主要内容

CSS 属性计算过程

你是否了解 CSS 的属性计算过程?

有的同学可能会说,CSS 属性我倒是知道,例如:

p {
color: red;
}

上面的 CSS 代码中,p 是元素选择器,color 就是其中的一个 CSS 属性。

但是要说 CSS 属性的计算过程,还真的不是很清楚。

没关系,通过此篇文章,你能够彻底明白什么是 CSS 属性的计算流程。

首先,不知道你有没有考虑过这样的一个问题,假设在 HTML 中有这么一段代码:

<body>
<h1>这是一个h1标题</h1>
</body>

上面的代码也非常简单,就是在 body 中有一个 h1 标题而已,该 h1 标题呈现出来的外观是如下:

目前我们没有设置该 h1 的任何样式,但是却能看到该 h1 有一定的默认样式,例如有默认的字体大小、默认的颜色。

那么问题来了,除了默认字体大小和默认颜色,这个 h1 元素上还有哪些属性呢?

**该元素上会有所有 CSS 属性。**你可以打开浏览器的开发者面板,选择【元素】,切换到【计算样式】,之后勾选【全部显示】,此时你就能看到在这个 h1 上所有 CSS 属性对应的值。

换句话说,我们书写的任何一个 HTML 元素,实际上都有一整套完整的 CSS 样式。这一点往往让初学者比较意外,因为我们平时书写 CSS 样式时,往往只会书写必要的部分,例如前面的:

p {
color: red;
}

这往往会给我们造成一种错觉,认为这个 p 元素上只有 color 属性。实际情况是,任何一个 HTML 元素都有一套完整的 CSS 样式,只不过你没有书写的样式,大部分情况下会使用其默认值。例如上图中 h1 一个样式都没有设置,全部使用默认值。

但是注意,我这里强调的是“大部分情况下”,难道还有我们“没有设置值,但是不使用默认值”的情况吗?

确实存在这种情况,所以了解“CSS 属性的计算过程”尤为重要。

属性值的计算过程分为四个步骤:

首先是确定声明值。所谓声明值就是作者自己书写的 CSS 样式,例如前面的:

p {
color: red;
}

这里我们声明了 p 元素为红色,那么就会应用此属性设置。

除了作者样式表,浏览器还存在“用户代理样式表”,简单来说就是浏览器内置的一套样式表。

在上面的示例中,作者样式表中设置了 color 属性,而用户代理样式表(浏览器提供的样式表)中设置了诸如 displaymargin-block-startmargin-block-endmargin-inline-startmargin-inline-end 等属性的值。

这些值目前没有冲突,因此最终会应用这些属性值。

接下来是层叠冲突。在确定声明值时,可能出现声明的样式规则发生冲突。

此时会进入解决层叠冲突的流程,这一步可以细分为三个步骤:

首先是比较源的重要性。当不同的 CSS 样式来源拥有相同的声明时,会根据样式表来源的重要性来确定应用哪一条样式规则。

样式表的源主要有三种:

浏览器会有一个基本的样式表来给任何网页设置默认样式,这些样式统称用户代理样式

网页的作者可以定义文档的样式,这是最常见的样式表,称之为页面作者样式

浏览器的用户,可以使用自定义样式表定制使用体验,称之为用户样式

对应的重要性顺序依次为页面作者样式高于用户样式,用户样式高于用户代理样式。

更详细的来源重要性比较,可以参阅 MDN

例如现在有页面作者样式表用户代理样式表中存在属性的冲突,页面作者样式表优先。

p {
color: red;
display: inline-block;
}

可以明显看到,作者样式表和用户代理样式表中同时存在的 display 属性设置,最终作者样式表覆盖了用户代理样式表中冲突的属性。这就是根据不同源的重要性决定应用哪一个源的样式。

其次是比较优先级。如果在同一个源中有样式声明冲突,就会进行样式声明的优先级比较。

例如:

<div class="test">
<h1>test</h1>
</div>
.test h1 {
font-size: 50px;
}

h1 {
font-size: 20px;
}

在上面的代码中,同属于页面作者样式,源的重要性相同,此时会以选择器的权重来比较重要性。

很明显,.test h1 的选择器权重要大于 h1,因此最终标题呈现为 50px

可以看到,落败的作者样式在 Elements>Styles 中会被划掉。

有关选择器权重的计算方式,可以参考 MDN

最后是比较次序。经历了上面两个步骤,大多数的样式声明能够被确定下来。但是还剩下最后一种情况,样式声明既是同源,权重也相同。

此时会比较样式声明的次序。

例如:

h1 {
font-size: 50px;
}

h1 {
font-size: 20px;
}

在上面的代码中,同样都是页面作者样式选择器的权重也相同,此时位于下面的样式声明会覆盖上面的样式声明,最终应用 20px 这一条属性值。

至此,样式声明中存在冲突的所有情况都被解决了。

接下来是使用继承。层叠冲突这一步完成后,解决了相同元素被声明了多条样式规则究竟应用哪一条样式规则的问题。

如果没有声明的属性,是否就使用默认值?

No、No、No,别急,还有第三个步骤,那就是使用继承而来的值。

例如:

<div>
<p>Lorem ipsum dolor sit amet.</p>
</div>
div {
color: red;
}

在上面的代码中,我们针对 div 设置了 color 属性值为红色,而针对 p 元素没有声明任何属性,但是由于 color 是可以继承的,因此 p 元素从最近的 div 继承到了 color 属性的值。

这里有两个点需要注意。

首先是最近的 div 元素,来看下面的例子:

<div class="test">
<div>
<p>Lorem ipsum dolor sit amet.</p>
</div>
</div>
div {
color: red;
}
.test {
color: blue;
}

这里并不涉及选中 p 元素声明 color 值,而是从父元素上继承 color 的值,因此是最近的元素决定继承的值。初学者往往会混淆,尝试比较权重,但实际上这里并不涉及权重比较,因为没有选中到 p 元素。

其次是哪些属性能够继承。关于这一点,可以在 MDN 上轻松查阅。例如以 text-align 为例,如下图所示:

最后是使用默认值。如果属性值都还不能确定下来,那么就只能使用默认值。

如下图所示:

前面提到,一个 HTML 元素要在浏览器中渲染出来,必须具备所有的 CSS 属性值,但绝大部分我们不会去设置,用户代理样式表里面也不会设置,也无法从继承拿到,因此最终都是使用默认值。

面试题

下面的代码,最终渲染出来的效果,a 元素是什么颜色?p 元素又是什么颜色?

<div>
<a href="">test</a>
<p>test</p>
</div>
div {
color: red;
}

大家能说出为什么会呈现这样的结果吗?

解答如下:

实际上原因很简单,因为 a 元素在用户代理样式表中已经设置了 color 属性的值,因此会应用此声明值。而在 p 元素中无论是作者样式表还是用户代理样式表,都没有对此属性进行声明,然而由于 color 属性是可以继承的,因此 p 元素的 color 属性值通过继承来自于父元素。