说实话,CSS 这玩意儿最让人头大的地方,往往不是怎么写,而是“为什么我写的样式没生效?”或者“明明写了大小,为什么没变?”。
很多初学者(甚至工作一两年的开发者)在面对布局混乱、样式被莫名覆盖时,脑子里都是一团浆糊。今天咱们不背教科书,就坐在沙发上,我给你把这事儿掰开了、揉碎了讲清楚。这就好比整理一个乱糟糟的衣柜,你得先知道衣服是怎么堆进去的,才能把它叠整齐。
一、 先解决最基础的问题:父元素设了字号,子元素没设,显示多大?
这个问题,答案其实很简单,但背后的原理能让你理解一半的 CSS 问题。
直接答案: 如果父元素设了 font-size,而子元素没有设置自己的 font-size,那么子元素会继承父元素的字号。
举个真实的例子
想象你在写一个博客页面。你在 body 里定义了基础的字体大小,然后你在文章里用了 <p> 标签,但忘记给 <p> 设字号了。
body {
font-size: 16px; /* 基础字号 */
}
p {
/* 这里我没写 font-size! */
color: #333;
}
结果: 你的段落文字依然是 16px。因为它从 body 那里“借”来了这个属性。
但是,这里有个巨大的坑,也是无数人踩坑的地方:继承的值不是绝对的像素值,而是计算后的值,并且它会层层传递,但单位 em 和 rem 会改变这种“继承”的性质。
关键点:em 不是“继承”那么简单
如果你用 em 单位,继承的逻辑会变得有点绕。看这个例子:
body {
font-size: 16px;
}
.parent {
font-size: 2em; /* 16px * 2 = 32px */
}
.child {
/* 没设 font-size */
font-size: 1.5em; /* 这个 1.5em 是相对于谁? */
}
分析:
body是 16px。.parent是2em,所以它的字号是16px * 2 = 32px。.child没设font-size,但它设了1.5em。注意!.child的1.5em是相对于它的父元素.parent的字号计算的,而不是相对于body。- 所以
.child的字号是32px * 1.5 = 48px。
这就是为什么理解“继承”和“计算值”的区别至关重要。 继承只是指“我没设,我用你的”,但一旦你设了相对单位(如 em),这个相对单位是基于当前元素的直接父元素的计算值,而不是基于祖先的原始值。
给小朋友的比喻: 想象你有一个大家庭。爷爷(
body)身高 170cm。爸爸(.parent)说:“我比爷爷高 2 倍”,所以爸爸是 340cm(当然,现实中不可能,但我们这是 CSS 世界)。你(.child)没说自己身高,但你说:“我比爸爸高 1.5 倍”。那你就是 340cm * 1.5 = 510cm。你继承的是爸爸的“身份”,但你的“身高计算”是跟爸爸比的,不是直接跟爷爷比的。
二、 哪些样式会被继承,哪些不会?
这是 CSS 继承的核心。不是所有属性都会向下传递。
1. 通常会被继承的属性(Textual & Font Properties)
这些属性通常跟“文字”本身有关,因为它们影响的是内容的呈现,而不是盒子本身。
color:颜色font-family:字体族font-size:字体大小font-weight:字体粗细font-style:斜体(italic)line-height:行高letter-spacing:字母间距word-spacing:单词间距text-align:文字对齐text-indent:首行缩进visibility:可见性(注意:display: none不会被子元素继承,但visibility: hidden会)
为什么这些会被继承? 因为它们是描述性的。比如,你说“这个段落是红色的”,那么段落里的每一个字都应该是红色的,对吧?这很自然。
2. 通常不会被继承的属性(Box & Layout Properties)
这些属性控制的是元素的“盒子”本身,包括位置、大小、边框等。
display:显示类型(block, inline, flex 等)width/height:宽高margin:外边距padding:内边距border:边框background:背景position:定位方式top,right,bottom,left:定位偏移float:浮动clear:清除浮动
为什么这些不会被继承? 因为它们是结构性的。比如,父亲说“我是浮在左边的”,儿子不一定非要浮在左边。父亲说“我有 10px 的边框”,儿子如果也继承 10px 边框,可能会撑破布局。每个元素都是独立的盒子,需要自己定义自己的结构和尺寸。
3. 容易混淆的“中间派”
有些属性在某些情况下看起来像被继承,但实际上行为很复杂。
list-style:通常不会被子元素继承,但列表项的内容(如<li>里的文字)会继承颜色等。cursor:鼠标指针形状,会继承。overflow:超出内容隐藏,不会继承。z-index:层叠顺序,不会继承。
一个小技巧来记忆:
如果这个属性是“让东西看起来怎么样”(文字、颜色、字体),它大概率会被继承。 如果这个属性是“东西在哪里、有多大、是什么形状”(位置、尺寸、布局),它大概率不会被继承。
三、 当继承遇见冲突:层叠顺序(Cascading)到底谁说了算?
现在问题来了:如果父元素设了 color: red,子元素没设,那子元素是红色。但如果我在子元素上也设了 color: blue,那谁赢?
这时候,CSS 的层叠(Cascading)规则就登场了。层叠不是随机打乱,而是有一套严格的优先级权重。
层叠优先级的核心公式
我们可以把选择器的权重想象成一个三位数的分数:(重要性, ID选择器数量, 类/属性/伪类选择器数量, 元素/伪元素选择器数量)。
但实际上,更简单的方法是记住这个金字塔,从低到高:
1. 用户代理样式表 (User Agent Stylesheet) - 浏览器默认的样式,如 <h1> 默认是 2em,加粗
2. 普通元素选择器 (Type Selectors) - 如 div, p, span
3. 类选择器、属性选择器、伪类选择器 (Class/Attribute/Pseudo-class) - 如 .box, [type="text"], :hover
4. ID 选择器 (ID Selectors) - 如 #header, #main
5. 内联样式 (Inline Styles) - 如 style="color: red;"
6. !important 声明 - 最高优先级(但有例外)
详细拆解:如何计算权重?
让我们用具体的例子来计算。
例子 1:谁赢?
div p {
color: blue;
}
.highlight {
color: red;
}
<div>
<p class="highlight">这段文字是什么颜色?</p>
</div>
分析:
div p的权重:1 个元素选择器 + 1 个元素选择器 =(0, 0, 0, 2).highlight的权重:1 个类选择器 =(0, 0, 1, 0)
比较: (0, 0, 1, 0) > (0, 0, 0, 2)
结论: 类选择器赢。文字是红色。
例子 2:ID vs 类
#main-content .text {
color: green;
}
.special {
color: yellow;
}
<div id="main-content">
<p class="text special">这段文字是什么颜色?</p>
</div>
分析:
#main-content .text的权重:1 个 ID + 1 个类 =(0, 1, 1, 0).special的权重:1 个类 =(0, 0, 1, 0)
比较: (0, 1, 1, 0) > (0, 0, 1, 0)
结论: ID 选择器所在的规则赢。文字是绿色。
例子 3:内联样式 vs ID
#header {
color: purple;
}
<div id="header" style="color: orange;">这段文字是什么颜色?</div>
分析:
#header的权重:1 个 ID =(0, 1, 0, 0)style="color: orange;"的权重:内联样式 =(1, 0, 0, 0)
比较: 内联样式的权重最高,因为它在第一位。 结论: 文字是橙色。
重要提示:权重是局部的,不是全局的
权重只在同一个属性发生冲突时才有意义。不同属性之间互不干扰。
.box {
color: red;
font-size: 20px;
}
#main .box {
color: blue;
font-size: 14px;
}
<div id="main">
<div class="box">测试文字</div>
</div>
分析:
- 对于
color:#main .box权重(0, 1, 1, 0)>.box权重(0, 0, 1, 0)。所以颜色是蓝色。 - 对于
font-size:同样,#main .box权重更高。所以字号是 14px。
四、 !important:那个“破坏者”
!important 是 CSS 中最强力的关键字,但它也是最容易滥用、导致代码难以维护的“毒药”。
语法
.box {
color: red !important;
}
!important 的优先级规则
!important 会将该声明的优先级提升到所有普通声明之上,但低于内联样式的 !important。
让我们重新排列一下优先级金字塔,把 !important 加进去:
1. 用户代理样式表
2. 普通样式表(作者样式表)
a. 普通选择器规则(按权重排序)
b. !important 声明(在所有普通规则之上)
3. 内联样式
a. 普通内联样式
b. !important 内联样式(最高!)
等等,我刚才说内联样式 > ID 选择器,那 !important 呢?
正确的顺序是:
- 用户代理样式(最低)
- 作者普通样式(按选择器权重排序)
- 作者
!important样式(比普通作者样式高,但比内联样式低) - 内联普通样式
- 内联
!important样式(最高)
修正之前的例子:
如果我把上面的例子改成:
#header {
color: purple !important; /* 作者样式中的 !important */
}
<div id="header" style="color: orange;">这段文字是什么颜色?</div>
分析:
#header规则带有!important,属于作者!important样式。- 内联样式
style="color: orange;"是内联普通样式。
比较: 内联普通样式 > 作者 !important 样式。
结论: 文字是橙色。内联样式依然赢,即使它有 !important。
再修正一个例子:
#header {
color: purple !important;
}
<div id="header" style="color: orange !important;">这段文字是什么颜色?</div>
分析:
- 作者
!important样式 - 内联
!important样式
比较: 内联 !important > 作者 !important。
结论: 文字是橙色。
为什么 !important 是毒药?
想象一下,你在写一个大型项目,有 10 个开发人员。
- 开发 A 说:“这个按钮应该是蓝色的。” 他写了
.btn { color: blue; } - 开发 B 发现没生效,因为别人的样式权重更高。他生气了,写了
.btn { color: blue !important; } - 开发 C 发现还是没生效,因为内联样式。他更生气了,写了
style="color: blue !important;" - 开发 D 想要黑色,他写了
!important。 - 开发 E 想要绿色,他也写了
!important。
结果,你的 CSS 里到处都是 !important,你完全不知道哪个样式会生效,修改起来极其痛苦。这就像在一个房间里放了 10 个老板,每个人都喊“听我的”,最后谁也听不清楚。
最佳实践:
尽量避免使用
!important。 如果必须使用,请确保它是内联的,并且只用于临时调试或覆盖第三方库的特定情况(如某些 UI 框架的强制覆盖)。
五、 实战演练:一个复杂的场景
让我们来看一个真实的、可能让你头疼的场景,然后一步步分析。
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-size: 16px;
color: black;
}
#main {
font-size: 1.2em; /* 相对于 body 的 16px,即 19.2px */
}
.content {
font-size: 0.8em; /* 相对于 #main 的 19.2px,即 15.36px */
color: blue !important;
}
.content p {
color: green;
}
.special {
color: red;
}
</style>
</head>
<body>
<div id="main">
<div class="content">
<p class="special">这段文字是什么颜色和大小?</p>
</div>
</div>
</body>
</html>
第一步:分析字号(font-size)
body的font-size是16px。#main的font-size是1.2em。因为#main是body的子元素,所以1.2em=16px * 1.2 = 19.2px。.content的font-size是0.8em。因为.content是#main的子元素,所以0.8em=19.2px * 0.8 = 15.36px。p没有设置font-size,所以它继承.content的font-size,即 15.36px。
结论:字号是 15.36px。
第二步:分析颜色(color)
这里有多层竞争,我们要仔细计算权重。
涉及的规则:
body { color: black; }— 权重:(0, 0, 0, 1)(1 个元素选择器).content { color: blue !important; }— 权重:这是一个!important声明,其基础权重是(0, 0, 1, 0),但因为!important,它提升了优先级。.content p { color: green; }— 权重:(0, 0, 2, 0)(1 个类 + 1 个元素选择器).special { color: red; }— 权重:(0, 0, 1, 0)(1 个类选择器)
目标元素: <p class="special">
分析:
- 规则 1(
body):color: black会被继承下来。但这只是默认的继承值
