CSS 层叠、继承与特异性
当你发现样式"不是我写的那样"时,通常是因为层叠(Cascade)、继承(Inheritance)或特异性(Specificity)在起作用。理解这三个概念,是掌握 CSS 的关键。
层叠的概念
"层叠"(Cascade)是 CSS 名称中的"C",它指的是当多个样式规则应用到同一个元素时,浏览器如何决定使用哪个样式。
层叠的规则
浏览器按照以下顺序决定样式的优先级:
- 来源优先级:用户样式 > 作者样式 > User Agent 样式
- 特异性:特异性高的规则覆盖特异性低的规则
- 出现顺序:后出现的规则覆盖先出现的规则(如果特异性相同)
/* 规则 1:特异性 = 1 */
p {
color: black;
}
/* 规则 2:特异性 = 10,后出现,所以覆盖规则 1 */
.highlight {
color: blue;
}
/* 规则 3:特异性 = 100,最高,所以覆盖规则 2 */
#special {
color: red;
}
<p class="highlight" id="special">这段文字是红色的</p>
结果:文字显示为红色,因为 #special 的特异性最高。
继承机制
继承(Inheritance)是指某些 CSS 属性会被子元素自动继承,而不需要为每个子元素单独设置。
可继承的属性
以下类型的属性通常会被继承:
- 文本属性:
color、font-family、font-size、font-weight、line-height、text-align、text-decoration、text-indent、text-transform、letter-spacing、word-spacing、white-space - 列表属性:
list-style、list-style-type、list-style-position - 表格属性:
border-collapse、border-spacing - 其他:
cursor、visibility
不可继承的属性
以下类型的属性通常不会被继承:
- 盒模型属性:
margin、padding、border、width、height - 定位属性:
position、top、right、bottom、left、z-index - 显示属性:
display、float、clear - 背景属性:
background-color、background-image - 其他:
opacity、transform、box-shadow
继承示例
body {
color: #333; /* 会被继承 */
font-family: Arial; /* 会被继承 */
font-size: 16px; /* 会被继承 */
margin: 0; /* 不会被继承 */
padding: 0; /* 不会被继承 */
background-color: #fff; /* 不会被继承 */
}
<body>
<p>这段文字会继承 body 的 color、font-family 和 font-size</p>
<!-- 但不会继承 margin、padding 和 background-color -->
</body>
控制继承
你可以使用以下关键字来控制继承:
inherit
强制继承父元素的值:
.child {
color: inherit; /* 继承父元素的 color */
}
initial
使用属性的初始值(浏览器默认值):
.child {
color: initial; /* 使用浏览器默认的 color 值 */
}
unset
如果是可继承属性,则继承;如果是不可继承属性,则使用初始值:
.child {
color: unset; /* 如果是可继承属性,继承;否则使用初始值 */
}
revert
恢复到用户样式表或浏览器默认样式:
.child {
color: revert; /* 恢复到浏览器默认或用户样式 */
}
特异性的计算规则
特异性(Specificity)是浏览器用来决定哪个 CSS 规则应该被应用的机制。当多个规则匹配同一个元素时,特异性高的规则会覆盖特异性低的规则。
特异性计算
特异性由四个部分组成,按优先级从高到低:
- 内联样式:特异性 = 1000
- ID 选择器:每个 ID 选择器 = 100
- 类选择器、属性选择器、伪类:每个 = 10
- 元素选择器、伪元素:每个 = 1
注意:通用选择器(*)和组 合选择器(>、+、~、空格)的特异性为 0。
计算示例
让我们通过一些例子来理解特异性的计算:
/* 特异性:1(1 个元素选择器) */
p {
color: black;
}
/* 特异性:10(1 个类选择器) */
.highlight {
color: blue;
}
/* 特异性:11(1 个类选择器 + 1 个元素选择器) */
p.highlight {
color: green;
}
/* 特异性:100(1 个 ID 选择器) */
#special {
color: red;
}
/* 特异性:111(1 个 ID + 1 个类 + 1 个元素) */
#special p.highlight {
color: purple;
}
/* 特异性:1000(内联样式) */
/* <p style="color: orange;">内联样式</p> */
特异性比较
当比较特异性时,从左到右比较每个部分:
/* 特异性:0,1,0,1 = 11 */
div p {
color: black;
}
/* 特异性:0,1,1,0 = 110 */
div .highlight {
color: blue; /* 这个会覆盖上面的 */
}
/* 特异性:0,2,0,1 = 201 */
div p.highlight {
color: green; /* 这个会覆盖上面的 */
}
比较规则:
- 先比较 ID 选择器的数量
- 如果相同,再比较类选择器的数量
- 如果相同,再比较元素选择器的数量
- 如果都相同,后出现的规则覆盖先出现的规则
特殊情况
!important
!important 会覆盖所有其他规则(除了另一个 !important 规则):
p {
color: black !important; /* 最高优先级 */
}
#special {
color: red; /* 即使特异性更高,也不会覆盖上面的 */
}
注意:!important 应该谨慎使用,因为它会破坏层叠规则,使代码难以维护。
内联样式
内联样式的特异性为 1000,高于所有选择器(除了 !important):
<p style="color: blue;">这段文字是蓝色的</p>
/* 即使这个规则的特异性很高,也不会覆盖内联样式 */
#special p.highlight {
color: red; /* 无效 */
}
实际冲突示例分析
让我们通过一些实际例子来分析样式冲突:
示例 1:特异性冲突
<div class="container">
<p class="highlight">这是一段文字</p>
</div>
/* 规则 1:特异性 = 1 */
p {
color: black;
}
/* 规则 2:特异性 = 10 */
.highlight {
color: blue; /* 这个会覆盖规则 1 */
}
/* 规则 3:特异性 = 11 */
p.highlight {
color: green; /* 这个会覆盖规则 2 */
}
结果:文字显示为绿色,因为 p.highlight 的特异性最高。
示例 2:出现顺序冲突
<p class="highlight">这是一段文字</p>
/* 规则 1:特异性 = 10 */
.highlight {
color: blue;
}
/* 规则 2:特异性 = 10(相同),但后出现 */
.highlight {
color: red; /* 这个会覆盖规则 1 */
}
结果:文字显示为红色,因为两个规则的特异性相同,后出现的规则覆盖先出现的规则。
示例 3:继承 vs 直接样式
<div class="parent">
<p>这是子元素</p>
</div>
/* 父元素样式 */
.parent {
color: blue; /* 会被继承 */
margin: 20px; /* 不会被继承 */
}
/* 子元素样式 */
p {
color: red; /* 直接样式,覆盖继承的蓝色 */
}
结果:文字显示为红色,因为直接样式覆盖继承样式。
示例 4:复杂特异性
<div id="container" class="wrapper">
<section class="content">
<p class="highlight">这是一段文字</p>
</section>
</div>
/* 规则 1:特异性 = 0,0,1,1 = 11 */
section p {
color: black;
}
/* 规则 2:特异性 = 0,0,2,1 = 21 */
section p.highlight {
color: blue; /* 这个会覆盖规则 1 */
}
/* 规则 3:特异性 = 0,1,1,1 = 111 */
#container section p {
color: green; /* 这个会覆盖规则 2 */
}
/* 规则 4:特异性 = 0,1,2,1 = 121 */
#container section p.highlight {
color: red; /* 这个会覆盖规则 3 */
}
结果:文字显示为红色,因为 #container section p.highlight 的特异性最高。
调试思路
当样式不符合预期时,可以按照以下思路调试: