层叠与特异性 — 多条规则打架时谁赢¶
这一章回答前端最经典的一问:「我写的样式为什么不生效?」 答案通常在这章:你的规则没输给人,是输给了另一条「更强」的规则, 或者赢了你却没生效的那条,写在文件更后面。读完你能算出任意两条规则谁赢, 并知道
!important为什么该当作核选项。
1. 这一章在全书链条里的位置¶
第 02 章解决了「选得中」。但只要项目稍大,同一个元素几乎必然被多条规则同时选中:
.highlight 说它绿,p 说它蓝,ID 说它红——浏览器必须有个判决程序。
这个判决程序就是 CSS 名字里那个 C(Cascading,层叠),它是全书三大基础概念之一,
与下一章的盒模型并列。继承是第二条通道:有些属性不用你写,自动从父辈流下来。
2. 顶层全景:一张判决流程图¶
一条元素碰上多条规则
│
├─ ① 有 !important 标记的声明?──── 有 → 用它(再比谁的特异性高)
│
├─ ② 比特异性四位分 ─── 分高者赢
│
├─ ③ 分相同?── 写在文件后面的赢(来源顺序)
│
└─ ④ 都没选中?── 看继承:该属性从父元素传下来了吗?传了就用传下来的值
│
└─ 没有 → 浏览器默认值
图说:①到③是「判决」,④是「兜底」。日常排查「样式不生效」,九成停在②③两步。
3. 核心原理:一层一层来¶
第一层:!important 横着来¶
color: blue !important; 这样标记的声明,压过除它自己之外的一切1:
书里那句输出解释很直白:哪怕 ID 选择器的特异性更高,!important 强迫段落保持蓝色2。
两个 !important 相撞怎么办?回到第 ② 层比特异性。所以 !important 不是免死金牌,
只是把比赛拖进加时。书里的使用纪律:最后一招(last resort),
用惯了会让代码极难维护——「!important 战争」是样式表烂掉的典型病征3。
第二层:特异性四位计分¶
每个选择器按组成算一个四位分 (a, b, c, d),从左到右逐级比4:
| 选择器成分 | 得分 | 例子 |
|---|---|---|
| 内联样式(style 属性) | 1, 0, 0, 0 | style="color: red;" |
| ID | 0, 1, 0, 0 | #header |
| 类 / 属性 / 伪类 | 0, 0, 1, 0 | .btn、[type="text"]、:hover |
| 元素 / 伪元素 | 0, 0, 0, 1 | p、::before |
通配 * |
0, 0, 0, 0 | 不计分 |
读法:从左边第一列开始比,大的赢;第一列相同才比第二列——像版本号,不像十进制数。
所以 0, 1, 0, 0(一个 ID)永远压过 0, 0, 15, 0(十五个类)。
主线例子:同一个 <p>,三条规则¶
输入:<p class="highlight" id="main-text">你好</p>,以及:
p { color: blue; } /* 分:0,0,0,1 —— 元素 */
.highlight { color: green; } /* 分:0,0,1,0 —— 类 */
#main-text { color: red; } /* 分:0,1,0,0 —— ID */
浏览器走一遍流程:三条都选中了它;都没 !important;比分,第一列就分出胜负——
ID 的 0,1,0,0 最大,文字红。书里演示的正是这个结论:
「the text will be red because the ID has the highest specificity score」5。
现在把第一条改成 p { color: blue !important; }——第 ① 层直接截胡,文字变蓝2。
这就是同一个输入在两层裁决下走出的两个结果:特异性是「分高者赢」,
!important 是「换个赛场,只许带 !important 的进场」。
第三层:来源顺序——同分时谁靠后谁赢¶
分一模一样时(比如两个类),写在 CSS 文件更后面的规则生效6。
注意是「写在文件后面」,不是「写在 HTML class 属性里靠后」:
<div class="blue red"> 的颜色由 .blue 和 .red 在 CSS 里谁后出现决定,
跟 HTML 属性里的排列无关7。这条规则解释了一个常见困惑:
「同样的两条规则,我调整了 CSS 里的先后,效果就变了。」
第四层:继承——不用选中也轮得到¶
上面三层都在解决「多条规则争一个元素」。继承解决的是另一个问题: 元素上没写任何规则时,有些属性的值会从父元素自动流下来。
会流的(书里列的):color、font-family、font-size、line-height、text-align
——全是文字类属性。不流的:margin、padding、border、width、height、background
——全是盒子类属性8。书里那个对照实验:
为什么是这个划分?文字属性跟着「内容」走,内容嵌套多深都还是那些字; 盒子属性跟着「每个元素自己」走,若边框都能继承,一个套一个的页面会画出一千层框。 规律记一句:和字有关的会传,和框有关的不传。
三个手动控制值,用来在继承上「纠偏」:
inherit——强制继承:把这个属性的值从父元素拿过来。书里的用途:按钮默认不继承正文的字体, 写button { font-family: inherit; font-size: inherit; }让它跟着走9。initial——回到这个属性的 CSS 默认值。unset——「该继承的继承,不该继承的回默认」,一句话两头兼顾。
4. 作者的判断与证据¶
- 「避免用 !important 解决特异性问题」——本章最重的纪律句,附理由(难维护), 给替代方案(写更具体的选择器,或干脆用类)3。
- 「选择器别嵌套太深:body div.container ul li.active a 这类写法 specificity 极高,日后无法覆盖」 ——经验断言,与第 02 章「最简单选择器」立场呼应10。
- 「样式不要用 ID」——第 02 章预告、本章兑现理由:ID 的 0,1,0,0 一锤定音,
类(0,0,1,0)再怎么加也打不过,只能上
!important——战争就是这么开始的11。 - 证据类型同前:全部为作者经验断言,无实验或数据。
5. 边界与局限¶
- 没讲特异性计分的边界情形(如
:is()、:where()对计分的特殊影响——:where()的计分是 0), 也没提层叠层(@layer)——现代 CSS 里比!important更体面的优先级管理手段,2022 年起已进入标准,本书未收。 - 没讲「 inherit 对表单元素的坑」之外更多的继承例外(如
a链接的颜色不继承—— 这是初学者撞完 body 颜色没传过去之后的第二撞,本书未提)。 - 本书把判决顺序表述为「Importance → Specificity → Source Order」三层12, 规范里还有更细的环节(如「出现顺序」与「用户/浏览器样式来源」的先后),速查粒度下省略了。
判断(我们的,不是书里的): 这是全书对「编程语言与系统研究」最有价值的一章: 特异性四位计分是「不带作用域的语言如何裁决全局冲突」的完整样本——分数按声明者的 「身份类别」而非出现位置加权,再加一个「后写覆盖先写」的平局规则, 这套设计与后来配置语言(YAML 覆盖、Tailwind 层序)里的优先级处理一脉相承。 如果错,会错在: 如果读者只需要「会用」,这套机制的细节(如 :where() 计分) 确属过度——本书选择不教,也是一种合理的取舍,不能算错。
6. 可带走的¶
- 判决三层:
!important→ 特异性四位分 → 写在后面的赢;都落空才轮到继承和默认值12。 - 四位分读法像版本号:内联 > ID > 类/属性/伪类 > 元素,从左边比;一个 ID 胜过任意多个类4。
- 「样式不生效」先查两件事:赢你的那条规则是谁(浏览器开发者工具里被划掉的就是它);它是不是写在更后面。
- 同分时拼的是 CSS 文件里的先后,不是 HTML class 属性里的先后7。
- 继承只传文字类属性(color、font-*、text-align),不传盒子类(margin、border、宽高、背景)8。
- 按钮字体不跟着 body 走,是继承的例外,
font-family: inherit一键纠偏9。 !important是核选项;频繁需要它,说明类名和结构该重排了3。
7. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 裁决三概念总览 | Chapter 3: Specificity & Cascade | text/04-ch01-1-chapter-overview.txt:7(搜「wins」) |
| 冲突规则(后写者赢) | 同上 | text/04-ch01-1-chapter-overview.txt:27(搜「Conflict Rule」) |
| 四位计分表 | 同上 | text/04-ch01-1-chapter-overview.txt:33(搜「four-part score」) |
| 主线例子(ID 赢) | 同上 | text/04-ch01-1-chapter-overview.txt:58(搜「highest specificity score」) |
| !important 语法与效果 | 同上 | text/04-ch01-1-chapter-overview.txt:60(搜「!important Rule」) |
| 继承属性 vs 非继承属性 | 同上 | text/04-ch01-1-chapter-overview.txt:82(搜「color, font-family」) |
| 三个手动控制值 | 同上 | text/04-ch01-1-chapter-overview.txt:88(搜「Forces an element」) |
| 层叠顺序三层 | 同上 | text/04-ch01-1-chapter-overview.txt:101(搜「Cascade Order」) |
| !important 战争 | 同上 | text/04-ch01-1-chapter-overview.txt:130(搜「!important War」) |
| 过度嵌套的坑 | 同上 | text/04-ch01-1-chapter-overview.txt:132(搜「Over-nesting」) |
| class 属性次序无关 | 同上 | text/04-ch01-1-chapter-overview.txt:134(搜「appears last in the CSS file」) |
| 按钮 font 纠偏 | 同上 | text/04-ch01-1-chapter-overview.txt:120(搜「Resetting Button」) |
-
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:60,搜「!important Rule」)。原文:「A declaration that overrides all other specificity calculations.」 ↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:73,搜「ignored」)。原文:「Even though the ID selector has higher specificity, the !important flag forces the paragraph to stay blue.」 ↩↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:130,搜「!important War」)。原文:「Avoid using !important to fix specificity issues. It makes the code very hard to maintain.」 ↩↩↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:33,搜「four-part score」)。原文:「Specificity is calculated using a four-part score (0, 0, 0, 0). The higher the number in the leftmost column, the more powerful the selector.」成分得分表见第 35–45 段。 ↩↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:58,搜「highest specificity score」)。原文:「If a <p> tag has the class highlight and the ID main-text, the text will be red because the ID has the highest specificity score.」 ↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:27,搜「Conflict Rule」)。原文:「If specificity and importance are equal, the rule written last in the CSS file wins.」 ↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:134,搜「appears last in the CSS file」)。原文:「If you have two classes on an element: <div class="blue red">, the color is determined by which class appears last in the CSS file, not the HTML attribute.」 ↩↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:82,搜「color, font-family」)。原文列出的继承项:color, font-family, font-size, line-height, text-align;非继承项:margin, padding, border, width, height, background。 ↩↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:120,搜「Resetting Button」)。原文:「Buttons do not inherit font settings from the body by default. You can fix this using inheritance.」 ↩↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:132,搜「Over-nesting」)。原文:「Writing selectors like body div.container ul li.active a creates extremely high specificity that is hard to override later. Keep selectors flat and simple.」 ↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:136,搜「ID Selector for Styling」)。原文:「Avoid using IDs for CSS. Because their specificity is so high, they are difficult to override. Use classes for everything related to design.」 ↩ -
出处:「Chapter 3: Specificity & Cascade」(
text/04-ch01-1-chapter-overview.txt:101,搜「Cascade Order」)。原文列出的三层:「1. Importance… 2. Specificity… 3. Source Order…」。 ↩↩