前言
开始之前先抛出三个疑问
- 为什么body会有自带8px的margin,来自于哪里。
- 为什么我们自己设置的margin会覆盖自带的margin。
- 为什么div是独占一行的,span不是
首先,让我们从最简单的代码开始,例如:
<div>hello world</div>
div{
color : red;
}
上面是一段很简单的代码,就一个div,然后设置了一个颜色属性为红色。
那么,我们这个div,只有color一个属性吗?
当然不是,真实情况是这个元素的所有属性都拥有,而且都设置有默认的属性值,一个属性都没落下。
通过谷歌浏览器我们可以进行查看
那这些默认的属性值又来自哪里呢。
来自与浏览器,浏览器内部给所有的html标签都设置了值。
谷歌浏览器源码地址:github.com/chromium/ch…
默认样式源码地址:github.com/chromium/ch…
通过默认样式文件可以看到,谷歌浏览器内部给body设置8px的margin。
给div设置了display:block。
拓展: 现在已经没有行级元素、块级元素的分类。html只负责语义化,css负责控制样式。所有独占一行的html元素都是设置了display:block的。现在的称呼叫行盒和块盒。
回到正题,我们要讨论一下这个属性值的计算过程是什么样子的。
总的来说,属性值的计算方法有下面四种,这也是属性值的计算顺序
- 确定声明值
- 层叠冲突
- 使用继承
- 使用默认值
确定声明值
当我们样式表中对某一个元素书写样式声明时,这个声明就会被当做css的属性值。 举个例子
<h1>hello world</h1>
在上面的代码中,我们没有书写任何的 CSS 样式,所以这个时候就采用浏览器的默认样式
font-size: 32px;
font-weight: 700
假设现在我们为这个 h1 设置一个样式:
font-size: 20px
这就是我们的作者样式,当作者样式和浏览器默认样式中的声明值有冲突时,会优先把作者样式中的声明值当作 CSS 的属性值。
而 font-weight 并没有和作者样式冲突,所以不受影响。
层叠冲突
此时会进入解决层叠冲突的流程。而这一步又可以细分为下面这三个步骤:
- 比较源的重要性
- 比较优先级(css权重)
- 比较次序
比较源的重要性
当不同的 CSS 样式来源拥有相同的声明时,此时就会根据样式表来源的重要性来确定应用哪一条样式规则。
那么问题来了,咱们的样式表的源究竟有几种呢?
整体来讲有三种来源:
- 浏览器会有一个基本的样式表来给任何网页设置默认样式(浏览器的默认样式)。这些样式统称用户代理样式。
- 网页的作者可以定义文档的样式,这是最常见的样式表(开发者撰写的样式),称之为页面作者样式。
- 读者,作为浏览器的用户,可以使用自定义样式表定制使用体验(例如设置皮肤),称之为用户样式。
对应的重要性顺序依次为:页面作者样式(开发者样式) > 用户样式(设置皮肤样式) > 用户代理样式(浏览器默认样式)
更详细的来源重要性比较,可以参阅 MDN:developer.mozilla.org/zh-CN/docs/…
我们来看一个示例。
例如现在有页面作者样式表(开发者样式)和用户代理样式表(浏览器默认样式)中存在属性的冲突,那么会以作者样式表优先。
p{
display: inline-block;
}
可以明显的看到,作者样式表和用户代理样式表中同时存在的 display 属性的设置,最终作者样式表干掉了用户代理样式表中冲突的属性。这就是第一步,根据不同源的重要性来决定应用哪一个源的样式。
比较优先级
其实本质就是 css选择器的权重问题。
那么接下来,如果是在同一个源中有样式声明冲突怎么办呢?此时就会进行样式声明的优先级比较。
例如:
<div class="test">
<h1>hello word</h1>
</div>
.test h1{
font-size: 50px;
}
h1 {
font-size: 20px;
}
在上面的代码中,同属于页面作者样式,源的重要性是相同的,此时会以选择器的权重来比较重要性。
很明显,上面的选择器的权重要大于下面的选择器,因此最终标题呈现为 50px。
可以看到,落败的作者样式在 Elements>Styles 中会被划掉。
有关选择器权重的计算方式,不清楚的同学,可以进入此传送门:developer.mozilla.org/en-US/docs/…
比较次序
说白了,当权重也相同时,哪个写在最下面,就用哪个
经历了上面两个步骤,大多数的样式声明能够被确定下来。但是还剩下最后一种情况,那就是样式声明既是同源,权重也相同。
此时就会进入第三个步骤,比较样式声明的次序。
举个例子:
h1 {
font-size: 50px;
}
h1 {
font-size: 20px;
}
在上面的代码中,同样都是页面作者样式,选择器的权重也相同,此时位于下面的样式声明会层叠掉上面的那一条样式声明,最终会应用 20px 这一条属性值。
至此,样式声明中存在冲突的所有情况,就全部被解决了。
使用继承
如果该条属性作者并没有设置,那么还不会着急去使用默认值,而是会去看一下能否继承到该属性值。例如:
<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:文本修饰(例如:underline、line-through等)text-indent:文本缩进text-transform:文本转换(例如:uppercase、lowercase等)white-space:空白符处理(例如:nowrap、pre等)word-spacing:单词间距
2. 盒模型相关的属性
border-collapse:表格边框合并方式border-spacing:表格单元格之间的间距box-sizing:盒子模型的计算方式cursor:鼠标指针的样式visibility:元素的可见性(例如:visible、hidden)
3. 位置与布局
direction:文本的书写方向(ltr或rtl)display:元素的显示类型position:元素的定位方式top、right、bottom、left:定位属性z-index:堆叠顺序
4. 表格相关的属性
caption-side:表格标题的位置empty-cells:表格中是否显示空单元格的边框border:表格单元格的边框border-radius:表格的边框圆角
5. 其他常见的继承属性
visibility:控制元素是否可见list-style:列表样式(包括类型、图片、位置等)page-break-after、page-break-before、page-break-inside:页面分页控制quotes:引用样式
使用默认值
最终,如果没有作者样式,该属性值也无法继承而来,则会使用浏览器的默认样式。
最后总结
- 当作者声明了样式,就用声明样式。
- 如果声明样式有冲突,就会按照css选择器的权重去解决冲突(层叠冲突)。
- 如果用户没有设置声明样式(也不会有层叠冲突),就会看当前css样式是否是可以继承的,可以继承就使用父类的样式,不能继承就使用浏览器设置的默认值。
-
为什么body会有自带8px的margin,来自于哪里。
因为浏览器会为每一个元素设置所有的样式,单独给body设置了8px的margin。 来自于浏览器的默认样式文件。
-
为什么我们自己设置的margin会覆盖自带的margin。
因为css属性的计算规则顺序,第一步就是确定声明值,确定了之后,没有层叠冲突的情况下,就不会往后走了。而使用浏览器默认样式是最后一步,根本就走不到这一步。
-
为什么div是独占一行的,span不是
因为浏览器默认样式文件里面,给div设置了display:block,使这个盒子变成了块盒,所有可以独占一行。而span设置了display:inline。如果去掉默认样式,div和span除去语义化,没有任何区别。