CSS属性的计算过程

272 阅读8分钟

前言

开始之前先抛出三个疑问

  1. 为什么body会有自带8px的margin,来自于哪里。
  2. 为什么我们自己设置的margin会覆盖自带的margin。
  3. 为什么div是独占一行的,span不是

首先,让我们从最简单的代码开始,例如:

<div>hello world</div>
div{
  color : red;
}

上面是一段很简单的代码,就一个div,然后设置了一个颜色属性为红色。

那么,我们这个div,只有color一个属性吗?

当然不是,真实情况是这个元素的所有属性都拥有,而且都设置有默认的属性值,一个属性都没落下。

通过谷歌浏览器我们可以进行查看

image.png

那这些默认的属性值又来自哪里呢。

来自与浏览器,浏览器内部给所有的html标签都设置了值。

谷歌浏览器源码地址:github.com/chromium/ch…

默认样式源码地址:github.com/chromium/ch…

image.png

通过默认样式文件可以看到,谷歌浏览器内部给body设置8px的margin。

给div设置了display:block。

拓展: 现在已经没有行级元素、块级元素的分类。html只负责语义化,css负责控制样式。所有独占一行的html元素都是设置了display:block的。现在的称呼叫行盒和块盒。


回到正题,我们要讨论一下这个属性值的计算过程是什么样子的。

总的来说,属性值的计算方法有下面四种,这也是属性值的计算顺序

  • 确定声明值
  • 层叠冲突
  • 使用继承
  • 使用默认值

确定声明值

当我们样式表中对某一个元素书写样式声明时,这个声明就会被当做css的属性值。 举个例子

<h1>hello world</h1>

在上面的代码中,我们没有书写任何的 CSS 样式,所以这个时候就采用浏览器的默认样式

font-size: 32px;
font-weight: 700

image.png

image.png 假设现在我们为这个 h1 设置一个样式:

font-size: 20px

image.png

这就是我们的作者样式,当作者样式和浏览器默认样式中的声明值有冲突时,会优先把作者样式中的声明值当作 CSS 的属性值。

font-weight 并没有和作者样式冲突,所以不受影响。

层叠冲突

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

  • 比较源的重要性
  • 比较优先级(css权重)
  • 比较次序
比较源的重要性

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

那么问题来了,咱们的样式表的源究竟有几种呢?

整体来讲有三种来源:

  • 浏览器会有一个基本的样式表来给任何网页设置默认样式(浏览器的默认样式)。这些样式统称用户代理样式
  • 网页的作者可以定义文档的样式,这是最常见的样式表(开发者撰写的样式),称之为页面作者样式
  • 读者,作为浏览器的用户,可以使用自定义样式表定制使用体验(例如设置皮肤),称之为用户样式

对应的重要性顺序依次为:页面作者样式(开发者样式) > 用户样式(设置皮肤样式) > 用户代理样式(浏览器默认样式)

更详细的来源重要性比较,可以参阅 MDNdeveloper.mozilla.org/zh-CN/docs/…

我们来看一个示例。

例如现在有页面作者样式表(开发者样式)和用户代理样式表(浏览器默认样式)中存在属性的冲突,那么会以作者样式表优先。

p{
  display: inline-block;
}

image.png

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

比较优先级

其实本质就是 css选择器的权重问题。

那么接下来,如果是在同一个源中有样式声明冲突怎么办呢?此时就会进行样式声明的优先级比较。

例如:

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

h1 {
  font-size: 20px;
}

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

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

image.png

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

有关选择器权重的计算方式,不清楚的同学,可以进入此传送门:developer.mozilla.org/en-US/docs/…

比较次序

说白了,当权重也相同时,哪个写在最下面,就用哪个

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

此时就会进入第三个步骤,比较样式声明的次序。

举个例子:


h1 {
  font-size: 50px;
}

h1 {
  font-size: 20px;
}

在上面的代码中,同样都是页面作者样式选择器的权重也相同,此时位于下面的样式声明会层叠掉上面的那一条样式声明,最终会应用 20px 这一条属性值。 image.png 至此,样式声明中存在冲突的所有情况,就全部被解决了。

使用继承

如果该条属性作者并没有设置,那么还不会着急去使用默认值,而是会去看一下能否继承到该属性值。例如:

<div class="test">
  <p>hello world</p>
</div>
.test{
  color:red
}

在上面的代码中,我们虽然没有在 p 段落上书写 color 属性,但是该属性能够从 div 上面继承而来,所以最终计算出来的值就是 red

可以继承的属性,都是可以设置为inherit的属性。以下是 CSS 中一些常见的可以使用 inherit 属性的属性

1. 文本相关的属性
  • color:文字的颜色
  • font-family:字体
  • font-size:字体大小
  • font-style:字体样式(例如:italic
  • font-variant:字体变种
  • font-weight:字体粗细
  • line-height:行高
  • letter-spacing:字母间距
  • text-align:文本对齐方式
  • text-decoration:文本修饰(例如:underlineline-through 等)
  • text-indent:文本缩进
  • text-transform:文本转换(例如:uppercaselowercase 等)
  • white-space:空白符处理(例如:nowrappre 等)
  • word-spacing:单词间距
2. 盒模型相关的属性
  • border-collapse:表格边框合并方式
  • border-spacing:表格单元格之间的间距
  • box-sizing:盒子模型的计算方式
  • cursor:鼠标指针的样式
  • visibility:元素的可见性(例如:visiblehidden
3. 位置与布局
  • direction:文本的书写方向(ltr 或 rtl
  • display:元素的显示类型
  • position:元素的定位方式
  • toprightbottomleft:定位属性
  • z-index:堆叠顺序
4. 表格相关的属性
  • caption-side:表格标题的位置
  • empty-cells:表格中是否显示空单元格的边框
  • border:表格单元格的边框
  • border-radius:表格的边框圆角
5. 其他常见的继承属性
  • visibility:控制元素是否可见
  • list-style:列表样式(包括类型、图片、位置等)
  • page-break-afterpage-break-beforepage-break-inside:页面分页控制
  • quotes:引用样式

使用默认值

最终,如果没有作者样式,该属性值也无法继承而来,则会使用浏览器的默认样式。

最后总结

  • 当作者声明了样式,就用声明样式。
  • 如果声明样式有冲突,就会按照css选择器的权重去解决冲突(层叠冲突)。
  • 如果用户没有设置声明样式(也不会有层叠冲突),就会看当前css样式是否是可以继承的,可以继承就使用父类的样式,不能继承就使用浏览器设置的默认值。
  1. 为什么body会有自带8px的margin,来自于哪里。

    因为浏览器会为每一个元素设置所有的样式,单独给body设置了8px的margin。 来自于浏览器的默认样式文件。

  2. 为什么我们自己设置的margin会覆盖自带的margin。

    因为css属性的计算规则顺序,第一步就是确定声明值,确定了之后,没有层叠冲突的情况下,就不会往后走了。而使用浏览器默认样式是最后一步,根本就走不到这一步。

  3. 为什么div是独占一行的,span不是

    因为浏览器默认样式文件里面,给div设置了display:block,使这个盒子变成了块盒,所有可以独占一行。而span设置了display:inline。如果去掉默认样式,div和span除去语义化,没有任何区别。