编程语言研究书库

层叠与特异性 — 多条规则打架时谁赢

层叠与特异性 — 多条规则打架时谁赢

这一章回答前端最经典的一问:「我写的样式为什么不生效?」 答案通常在这章:你的规则没输给人,是输给了另一条「更强」的规则, 或者赢了你却没生效的那条,写在文件更后面。读完你能算出任意两条规则谁赢, 并知道 !important 为什么该当作核选项。

1. 这一章在全书链条里的位置

第 02 章解决了「选得中」。但只要项目稍大,同一个元素几乎必然被多条规则同时选中: .highlight 说它绿,p 说它蓝,ID 说它红——浏览器必须有个判决程序。 这个判决程序就是 CSS 名字里那个 C(Cascading,层叠),它是全书三大基础概念之一, 与下一章的盒模型并列。继承是第二条通道:有些属性不用你写,自动从父辈流下来。

2. 顶层全景:一张判决流程图

一条元素碰上多条规则
   │
   ├─ ① 有 !important 标记的声明?──── 有 → 用它(再比谁的特异性高)
   │
   ├─ ② 比特异性四位分 ─── 分高者赢
   │
   ├─ ③ 分相同?── 写在文件后面的赢(来源顺序)
   │
   └─ ④ 都没选中?── 看继承:该属性从父元素传下来了吗?传了就用传下来的值
              │
              └─ 没有 → 浏览器默认值

图说:①到③是「判决」,④是「兜底」。日常排查「样式不生效」,九成停在②③两步。

3. 核心原理:一层一层来

第一层:!important 横着来

color: blue !important; 这样标记的声明,压过除它自己之外的一切1:

p          { color: blue !important; }
#main-text { color: red; }   /* 被无视:特异性再高也打不过 !important */

书里那句输出解释很直白:哪怕 ID 选择器的特异性更高,!important 强迫段落保持蓝色2。 两个 !important 相撞怎么办?回到第 ② 层比特异性。所以 !important 不是免死金牌, 只是把比赛拖进加时。书里的使用纪律:最后一招(last resort), 用惯了会让代码极难维护——「!important 战争」是样式表烂掉的典型病征3。

第二层:特异性四位计分

每个选择器按组成算一个四位分 (a, b, c, d),从左到右逐级比4:

选择器成分得分例子
内联样式(style 属性)1, 0, 0, 0style="color: red;"
ID0, 1, 0, 0#header
类 / 属性 / 伪类0, 0, 1, 0.btn、[type="text"]、:hover
元素 / 伪元素0, 0, 0, 1p、::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。书里那个对照实验:

body {
  color: darkgray;            /* 会继承:body 里的文字全是深灰 */
  border: 1px solid black;    /* 不继承:只有 body 自己带边框 */
}

为什么是这个划分?文字属性跟着「内容」走,内容嵌套多深都还是那些字; 盒子属性跟着「每个元素自己」走,若边框都能继承,一个套一个的页面会画出一千层框。 规律记一句:和字有关的会传,和框有关的不传。

三个手动控制值,用来在继承上「纠偏」:

  • 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. 可带走的

  1. 判决三层:!important → 特异性四位分 → 写在后面的赢;都落空才轮到继承和默认值12。
  2. 四位分读法像版本号:内联 > ID > 类/属性/伪类 > 元素,从左边比;一个 ID 胜过任意多个类4。
  3. 「样式不生效」先查两件事:赢你的那条规则是谁(浏览器开发者工具里被划掉的就是它);它是不是写在更后面。
  4. 同分时拼的是 CSS 文件里的先后,不是 HTML class 属性里的先后7。
  5. 继承只传文字类属性(color、font-*、text-align),不传盒子类(margin、border、宽高、背景)8。
  6. 按钮字体不跟着 body 走,是继承的例外,font-family: inherit 一键纠偏9。
  7. !important 是核选项;频繁需要它,说明类名和结构该重排了3。

7. 原文地图

主题原书章原文位置
裁决三概念总览Chapter 3: Specificity & Cascadetext/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」)

Footnotes

  1. 出处:「Chapter 3: Specificity & Cascade」(text/04-ch01-1-chapter-overview.txt:60,搜「!important Rule」)。原文:「A declaration that overrides all other specificity calculations.」 ↩

  2. 出处:「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.」 ↩ ↩2

  3. 出处:「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.」 ↩ ↩2 ↩3

  4. 出处:「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 段。 ↩ ↩2

  5. 出处:「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.」 ↩

  6. 出处:「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.」 ↩

  7. 出处:「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.」 ↩ ↩2

  8. 出处:「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。 ↩ ↩2

  9. 出处:「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.」 ↩ ↩2

  10. 出处:「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.」 ↩

  11. 出处:「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.」 ↩

  12. 出处:「Chapter 3: Specificity & Cascade」(text/04-ch01-1-chapter-overview.txt:101,搜「Cascade Order」)。原文列出的三层:「1. Importance… 2. Specificity… 3. Source Order…」。 ↩ ↩2

On this page