CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
HTML 与 CSS 基础/课时/CSS 选择器与层叠
RawBlame
课时目录12 节

HTML 与 CSS 基础

  1. 01网页如何工作
  2. 02HTML 文档结构
  3. 03文本、链接与媒体
  4. 04HTML 表单
  5. 05语义化与可访问性
  6. 06CSS 选择器与层叠
  7. 07盒模型与布局流
  8. 08Flexbox 布局
  9. 09Grid 布局
  10. 10响应式设计
  11. 11综合练习:静态课程页
  12. 12框架与下一步

CSS 选择器与层叠

选择器、优先级、继承与变量。

35 分钟

CSS 基础

这一课你将能做到

  1. 使用选择器(元素、类、ID、属性、后代、组合器)精确选中页面元素
  2. 理解层叠、继承和特异性的计算规则,能预测样式冲突时的最终结果
  3. 使用 CSS 自定义属性(变量)管理颜色、间距等设计令牌,写出可维护的样式代码

为什么要学

HTML 搭好了骨架,CSS 负责让页面「好看」。但 CSS 不是简单的「键值对装饰语言」——它的核心机制是层叠(Cascading),即当多个规则同时作用于一个元素时,浏览器如何决定哪个生效。不理解层叠、继承和特异性,你会在「为什么我的样式不生效」「为什么改了这里那里也变了」的问题中反复挣扎。

CodeForge 的课程卡片列表有统一的品牌色、间距和圆角,这些值在几十个地方重复出现。使用 CSS 自定义属性(--primary: #2563eb)可以把这些值集中管理,改一处全局生效,避免「漏改一个地方导致颜色不一致」的尴尬。

先建立图像

浏览器如何决定元素最终样式?

1. 来源:浏览器默认样式 < 用户样式 < 作者样式(你的 CSS)< !important
2. 层叠:后写的覆盖先写的(同优先级、同特异性时)
3. 特异性:行内 > ID > 类/属性/伪类 > 元素/伪元素
4. 继承:部分属性(如 color、font-family)自动继承父元素
          部分属性(如 margin、padding)不继承

跟着做

步骤 1:选择器类型与用法

创建 css-intro.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CSS 基础练习</title>
  <style>
    /* 元素选择器 */
    p { line-height: 1.6; }

    /* 类选择器 */
    .card { background: white; border-radius: 12px; padding: 1.5rem; }

    /* ID 选择器 */
    #hero { text-align: center; padding: 3rem 1rem; }

    /* 后代选择器:nav 里面的所有 a */
    nav a { color: #334155; text-decoration: none; }

    /* 子代选择器:ul 的直接子元素 li */
    ul > li { margin-bottom: 0.5rem; }

    /* 相邻兄弟选择器:紧跟 h2 后面的第一个 p */
    h2 + p { color: #64748b; font-size: 0.9375rem; }

    /* 通用兄弟选择器:h2 后面的所有 p */
    h2 ~ p { margin-top: 0.5rem; }

    /* 属性选择器 */
    input[type="email"] { border: 1px solid #cbd5e1; }
    a[href^="https"] { color: #2563eb; } /* 以 https 开头 */
    a[href$=".pdf"] { color: #dc2626; }   /* 以 .pdf 结尾 */

    /* 伪类 */
    a:hover { text-decoration: underline; }
    button:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }
    li:first-child { font-weight: 600; }

    /* 伪元素 */
    h1::after {
      content: "";
      display: block;
      width: 60px;
      height: 4px;
      background: #2563eb;
      margin-top: 0.75rem;
      border-radius: 2px;
    }
  </style>
</head>
<body>
  <nav>
    <ul>
      <li><a href="/">首页</a></li>
      <li><a href="/courses">课程</a></li>
      <li><a href="https://example.com">外部链接</a></li>
    </ul>
  </nav>

  <section id="hero">
    <h1>CSS 基础精讲</h1>
    <p>掌握选择器、层叠与现代样式管理。</p>
  </section>

  <section class="card">
    <h2>课程特色</h2>
    <p>实战驱动,每课都有可运行代码。</p>
    <p>覆盖 Flexbox、Grid、响应式设计。</p>
  </section>

  <form>
    <input type="email" placeholder="邮箱">
    <input type="password" placeholder="密码">
  </form>
</body>
</html>

保存并打开,观察不同选择器的效果。特别留意 h2 + p 只选中紧跟在 h2 后面的第一个 p,而 h2 ~ p 选中 h2 后面所有同级的 p。

步骤 2:理解特异性(Specificity)

<style>
  /* 特异性:0-1-0(一个类) */
  .title { color: blue; }

  /* 特异性:0-2-0(两个类) */
  .card .title { color: green; }

  /* 特异性:1-0-0(一个 ID) */
  #main-title { color: red; }

  /* 特异性:1-1-0(一个 ID + 一个类),最高 */
  #main-title.title { color: purple; }
</style>

<h1 id="main-title" class="title">这段文字是什么颜色?</h1>

答案是紫色。特异性计算采用 (a, b, c) 三段式:

  • a = 行内样式数(style="...")
  • b = ID 选择器数
  • c = 类选择器 + 属性选择器 + 伪类数
  • 最后比较元素/伪元素数(通常不写出)

比较时从 a 开始,大的胜出;a 相同再比 b,以此类推。

步骤 3:层叠与继承

<style>
  body {
    color: #334155;      /* 被继承 */
    font-family: system-ui, sans-serif; /* 被继承 */
    margin: 0;           /* 不被继承 */
  }
  .highlight {
    color: #2563eb;      /* 覆盖继承的 color */
  }
</style>

<body>
  <p>这段文字继承 body 的颜色。</p>
  <p class="highlight">这段文字是蓝色。</p>
</body>

color、font-family、line-height、text-align 等属性默认继承。margin、padding、border、background、width 等默认不继承。可用 inherit 强制继承,或用 initial 重置为默认值。

步骤 4:使用 CSS 自定义属性

<style>
  :root {
    --primary: #2563eb;
    --primary-hover: #1d4ed8;
    --text-main: #0f172a;
    --text-muted: #64748b;
    --surface: #ffffff;
    --radius: 12px;
    --space-sm: 0.5rem;
    --space-md: 1rem;
    --space-lg: 2rem;
  }

  @media (prefers-color-scheme: dark) {
    :root {
      --text-main: #f8fafc;
      --surface: #1e293b;
    }
  }

  .btn {
    background: var(--primary);
    color: white;
    padding: var(--space-sm) var(--space-md);
    border-radius: var(--radius);
  }
  .btn:hover {
    background: var(--primary-hover);
  }
</style>

自定义属性(CSS Variables)在 :root(等价于 <html>)中定义时是全局的,在任意选择器中定义时是局部作用域。配合 @media (prefers-color-scheme: dark) 可以自动适配系统深色模式。

讲清楚要点

  1. 选择器的精确度与性能:ID 选择器在 CSS 中特异性很高,但因其不可复用,实际项目中应优先使用类选择器。过于复杂的选择器(如 div.container > ul.nav li.item a span)不仅难以维护,还会增加浏览器匹配计算量。

  2. 特异性不是分数,是元组:(0, 1, 0) 不是「10分」,(0, 0, 10) 也不是「10分」。(0, 1, 0) 永远大于 (0, 0, 99),因为 ID 的权重层级高于类。不要试图用「增加类名数量」来击败 ID。

  3. !important 是最后手段:它的特异性高于一切普通规则,但会破坏正常的层叠逻辑,导致后续维护极其困难。仅在覆盖第三方库内联样式或极少数硬需求时使用。

  4. 继承减少重复声明:如果整个页面使用相同字体和文字颜色,在 body 或 :root 上设置一次即可,不用每个元素都写。需要覆盖时,在特定选择器中重写。

  5. 自定义属性让主题切换变得简单:修改 :root 中的 --primary 值,所有引用它的按钮、链接、边框色会同步更新。这比全局搜索替换颜色值安全得多,也支持运行时通过 JavaScript 修改(document.documentElement.style.setProperty('--primary', 'red'))。

  6. :focus-visible 替代 :focus::focus 会在鼠标点击后也显示焦点轮廓,视觉上不美观,导致很多开发者粗暴地 outline: none,严重损害键盘可访问性。:focus-visible 只在键盘聚焦时显示轮廓,是现代最佳实践。

你可能会懵的地方

  • 「类选择器和 ID 选择器哪个好?」 优先用类选择器。ID 在页面中只能出现一次,无法复用样式;且 ID 特异性过高,后续很难覆盖。ID 的主要用途是页内锚点(<a href="#section">)和 JavaScript 操作(getElementById)。

  • 「为什么后写的 CSS 会覆盖先写的?」 这是层叠(Cascading)的规则之一:当来源和特异性相同时,后声明的规则胜出。这也是为什么 CSS 文件通常按「基础 → 组件 → 工具类」的顺序引入,确保后面的能覆盖前面的。

  • 「* { margin: 0; padding: 0; } 这种重置好吗?」 过于粗暴。* 选择器会匹配所有元素,包括你没想到的(如 dialog、input),且特异性为 0,容易被意外覆盖。现代推荐用更精确的重置,如 *, *::before, *::after { box-sizing: border-box; } 配合特定元素的 margin/padding 重置。

  • 「CSS 变量和预处理器变量(Sass/Less)有什么区别?」 CSS 自定义属性是原生支持的,运行时可用 JS 读写,支持层叠和作用域,浏览器能实时计算。Sass 变量在编译时替换为静态值,运行时无法修改。两者可以共存:Sass 管理编译期常量,CSS 变量管理运行时主题。

  • 「rem、em、px 到底怎么选?」 px 是绝对单位,适合边框(1px)。rem 相对于根元素(<html>)的字体大小,适合间距和尺寸,配合根元素字体大小调整可实现整体缩放。em 相对于父元素的字体大小,适合组件内部局部缩放(如按钮的内边距随按钮字号变化)。

错法对照

错法问题正解
滥用 ID 选择器写样式无法复用,特异性过高,难以覆盖用类选择器定义样式,ID 留给 JS 和锚点
用 !important 解决所有冲突破坏层叠,后续维护噩梦先检查选择器特异性,通过增加特异性或调整顺序解决
给所有元素写 margin: 0破坏表单、列表等默认样式使用现代 CSS Reset(如 Normalize.css 或自己写针对性重置)
选择器嵌套过深性能差,耦合度高保持扁平,优先使用类选择器
颜色值硬编码在几十处改品牌色时漏改、错改使用 CSS 自定义属性集中管理
用 :focus { outline: none; }键盘用户无法看到焦点用 :focus-visible 替代,或提供自定义焦点样式

卡点急救

  1. 样式写了但不生效:按 F12 → Elements → 选中元素 → Styles 面板,看目标规则是否被划掉。如果被划掉,看覆盖它的规则在哪里(特异性更高或更靠后)。也可以点击「Computed」标签看最终计算值。

  2. 修改一个颜色,其他地方也变了:检查是否用了元素选择器(如 p { color: red; })影响到了不相关的段落。解决方案:给需要特殊样式的元素加类名,用类选择器限定范围。

  3. 深色模式下文字颜色不对:检查是否使用了硬编码的 color: #000 而没有适配 prefers-color-scheme。把所有颜色提取到 CSS 自定义属性中,在 @media (prefers-color-scheme: dark) 中覆盖对应变量。

面试常问

  • 「CSS 选择器的优先级(特异性)如何计算?」 特异性按 (a, b, c) 三段元组计算:a 为行内样式数量;b 为 ID 选择器数量;c 为类选择器、属性选择器和伪类数量。比较时从 a 到 c 依次比较,大的胜出。例如 #app .title 的特异性是 (0, 1, 1),大于 .card .title 的 (0, 0, 2)。!important 优先级最高,但应避免滥用。

  • 「CSS 的层叠顺序是怎样的?」 层叠按三个维度决定:一、来源和重要性(浏览器默认 < 用户样式 < 作者样式 < !important);二、特异性(选择器的权重);三、源代码顺序(同优先级同特异性时,后写的覆盖先写的)。理解这三个维度就能预测任何样式冲突的结果。

  • 「CSS 自定义属性有什么优势?如何在运行时修改?」 优势:原生支持无需编译;支持作用域和继承;可通过媒体查询响应环境变化(如深色模式);可在运行时用 JavaScript 动态修改。JS 修改方式:document.documentElement.style.setProperty('--primary', '#dc2626')。修改后,所有引用该变量的元素会自动更新,无需逐个选择器修改。

自测三问

  1. 一个元素同时被 #app .title、section h1 和 h1[style="color:red"] 选中,最终颜色由哪个规则决定?为什么?
  2. 为什么生产环境中推荐用类选择器而不是 ID 选择器来定义样式?
  3. 你想实现一个跟随系统深色模式自动切换的主题,应该使用什么 CSS 技术?

小结一句话

CSS 的核心不是「把文字变红」,而是「精确选中目标元素并理解浏览器如何裁决冲突」;掌握选择器、特异性、继承和自定义属性,才能写出可预测、可维护的现代样式代码。

延伸

  • MDN:CSS 选择器
  • MDN:层叠与继承
  • MDN:CSS 自定义属性
  • 下一课:CSS 盒模型——一切元素都是盒子。
加载本节练习…
← 上一课语义化与可访问性下一课 →盒模型与布局流