freeCodeCamp Basic-CSS 实战:用 id 选择器覆盖 Class 声明,理解 CSS 优先级机制
本篇基于 freeCodeCamp 课程中 basic-css 模块的实战练习 Override Class Declarations by Styling ID Attributes,讲解当同一个元素同时命中多个 CSS 声明时的冲突裁决规则:为什么"后声明的 class"会覆盖"先声明的 class",以及如何通过为元素添加 id 属性并编写 #id 选择器,无条件地压过所有 class 级声明。读完后你将掌握 id 选择器的写法、class 声明顺序与 HTML 中使用顺序的区别,并能用计算样式(getComputedStyle)验证覆盖结果。
前置背景:这条课程线的演进脉络
该练习位于课程文件 curriculum/structure/superblocks/responsive-web-design.json 所定义的 Responsive Web Design 超级块下的 basic-css 区块,是其中"样式优先级"三连击的中间一环。从 challengeOrder 的顺序 可以看到这条知识链:
- Prioritize One Style Over Another(源文件):创建
.pink-textclass 并应用到h1,证明 class 声明可以覆盖body元素继承下来的color: green。 - Override Styles in Subsequent CSS(源文件):在
.pink-text之后再声明.blue-text,同时把两个 class 加到同一个h1上,证明声明顺序靠后者获胜,而与 HTML 中 class 的书写顺序无关。 - 本篇:Override Class Declarations by Styling ID Attributes:进一步用
#orange-textid 声明覆盖上述两个 class 的声明。
本篇练习结束后,课程还会继续讲解用内联 style 属性(Override Class Declarations with Inline Styles)和 !important(Override All Other Styles by using Important)覆盖声明,共同构成完整的优先级阶梯。
而 id 选择器本身并非本篇首次出现——在更早的 Use an id Attribute to Style an Element 练习中已经建立了两个关键认知:id 不可复用、只应用于一个元素;id 的优先级(specificity)高于 class,两者冲突时 id 的样式胜出。本篇正是在此基础上做的实战验证。
核心概念回顾:声明顺序决定 class 冲突的赢家
练习文档开头的 description 部分首先回顾上一节的结论:浏览器按 CSS 声明在 <style> 元素中的书写顺序自上而下读取,冲突时采用最后声明的那条。这里有一个容易被忽视的细节:
即使我们在
h1元素的 class 列表中把blue-text写在pink-text前面,浏览器看的依然是声明顺序,而不是它们被使用的顺序!
这一点与上一节练习中的 Note 呼应:"It doesn't matter which order the classes are listed in the HTML element. However, the order of the class declarations in the <style> section is what is important."
练习任务:给 h1 添加 id 并编写 id 声明
本篇练习的具体目标,是把 h1 的文字变成橙色,手段是绕开 class 声明顺序——给元素一个 id,然后为这个 id 写 CSS 声明。文档给出的操作步骤如下:
-
给
h1元素添加id属性,值为orange-text。id 属性在 HTML 中的写法形如:<h1 id="orange-text"> -
保留
h1上原有的blue-text和pink-text两个 class,不要移除。 -
在
style元素中为orange-text这个 id 创建 CSS 声明。id 选择器的语法是在 id 名前加#,文档给出的示例:#brown-text { color: brown; }按同样格式,你需要写的是:
#orange-text { color: orange; }
文档特别强调了一条关键规则,也是本篇的题眼:
Note: 无论你把这条 CSS 声明写在
.pink-textclass 的上面还是下面,都没有关系——因为id属性声明永远优先(theidattribute will always take precedence)。
这意味着 id 声明的胜负与声明顺序解耦了,这与上一节"后声明者胜"的 class 规则形成鲜明对比。
种子代码与参考答案
练习的起点是 --seed-- 中给出的初始代码,此时 h1 是蓝色(.blue-text 声明在后):
<style>
body {
background-color: black;
font-family: monospace;
color: green;
}
.pink-text {
color: pink;
}
.blue-text {
color: blue;
}
</style>
<h1 class="pink-text blue-text">Hello World!</h1>
完成练习后的参考答案(--solutions--)如下,可以看到两处改动:<style> 中新增 #orange-text 声明,以及 h1 标签上新增 id="orange-text":
<style>
body {
background-color: black;
font-family: monospace;
color: green;
}
.pink-text {
color: pink;
}
.blue-text {
color: blue;
}
#orange-text {
color: orange;
}
</style>
<h1 id="orange-text" class="pink-text blue-text">Hello World!</h1>
注意两个 class 依然保留在元素上,h1 实际命中的声明有四条(body 继承色、.pink-text、.blue-text、#orange-text),最终渲染为橙色,正是 id 优先级高于 class 的直观证据。
验收标准:文档内置的自动化断言
该练习文件以 frontmatter + 标记区块(--description--、--instructions--、--hints--、--seed--、--solutions--)的结构组织,其中 --hints-- 区块内嵌了 7 条 JavaScript 断言,构成了客观的验收标准。逐条拆解如下:
| 断言 | 校验内容 |
|---|---|
classList.contains('pink-text') |
h1 必须仍带有 pink-text class |
classList.contains('blue-text') |
h1 必须仍带有 blue-text class |
getAttribute('id') === 'orange-text' |
h1 的 id 必须为 orange-text |
lengthOf(queryAll('h1'), 1) |
页面只能有一个 h1 元素 |
assert.match(..., /#orange-text\s*{/gi) |
源码中必须存在 #orange-text { 声明(先经 removeCssComments 去除注释再匹配) |
notMatch(..., /<h1.*style.*>/gi) |
h1 上不允许出现 style 属性——本篇禁用内联样式这条捷径 |
getComputedStyle(h1)['color'] === 'rgb(255, 165, 0)' |
计算样式必须是橙色 rgb(255, 165, 0),即 orange 的标准 RGB 值 |
最后一条断言特别值得玩味:它不检查你写的是 orange 还是 rgb(255, 165, 0),而是读取浏览器最终计算出的样式值。orange 会被解析为 rgb(255, 165, 0),所以两种写法都能通过;但若 id 声明写错或被 class 覆盖导致颜色不是橙色,断言立刻失败。配合第二条"禁用 style 属性"的断言,这套检查保证了练习真正在验证"用 id 声明覆盖 class 声明"这一知识点,而非其他取巧手段。
优先级阶梯小结
结合 basic-css 区块中这几道连续练习的实验结果,从源码结构看,课程的结论可以整理为如下优先级阶梯(从高到低),且高优先级与声明位置无关:
内联 style 属性 (下一篇验证,见 Override Class Declarations with Inline Styles)
!important 声明 (Override All Other Styles by using Important)
id 选择器(#orange-text) ← 本篇验证:无条件压过所有 class 声明
后声明的 class(.blue-text) (Override Styles in Subsequent CSS:同级 class 按声明顺序)
先声明的 class(.pink-text)
元素选择器(body 继承的 color: green)
本篇练习的价值在于用一次最小改动(加一个 id、三条 CSS)实证了阶梯中"id 高于 class"这一层,并与前后练习形成可对照的实验序列:同一元素、同一组冲突声明,仅改变覆盖手段,观察 getComputedStyle 读到的 color 值从 rgb(0, 0, 255)(蓝)变为 rgb(255, 165, 0)(橙),再在后续练习中变为 rgb(255, 255, 255)(白,内联样式)。
需要说明的是,freeCodeCamp 课程文档对"id 永远优先于 class"的表述是针对"单一 id 对多个 class"这一常见场景的教学简化;完整的 CSS 规范优先级(specificity)计算还涉及属性选择器、伪类、多层选择器的叠加比较等,可推断在后续更高级的课程模块中会进一步展开。就本篇练习而言,掌握"id 声明不受书写位置影响地覆盖 class 声明"这一结论,已足以应对绝大多数日常开发中的样式覆盖需求。
延伸阅读路径
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00