第14课:选择器(上)——基础选择器、组合选择器、属性选择器、伪类选择器
CSS 选择器是连接 HTML 结构与视觉样式的桥梁。它决定了“哪些元素应该被赋予样式”。选择器的精准度直接影响代码的可维护性、可复用性以及样式冲突的概率。本节课将系统讲解四类核心选择器的语法、行为与使用场景:从最基础的类型和类选择器,到能匹配复杂模式的属性选择器和伪类选择器。
1. 基础选择器:四种最基本的元素定位方式
基础选择器是所有选择器组合的原子单位。掌握它们的权重与行为,是理解更复杂选择器的前提。
1.1 通配选择器(Universal Selector)
* 匹配文档中所有元素。
1 | * { |
| 特性 | 说明 |
|---|---|
| 权重 | 0, 0, 0(最低,不贡献任何权重)。 |
| 性能 | 现代浏览器对 * 有专门优化,性能影响可忽略。但应避免 * 后跟后代选择器(如 * p),这会导致遍历所有元素。 |
| 典型用途 | 全局盒模型重置、移除默认边距。 |
1.2 类型选择器(Type Selector)
通过 HTML 标签名匹配元素。
1 | p { |
| 特性 | 说明 |
|---|---|
| 权重 | 0, 0, 1(每个元素名贡献一位权重)。 |
| 适用场景 | 为某一类标签设置基础样式(如统一所有段落行高)。 |
1.3 类选择器(Class Selector)
通过 HTML 元素的 class 属性值匹配。语法为 . 后跟类名。
1 | <p class="intro highlight">首段介绍文字</p> |
1 | .intro { |
| 特性 | 说明 |
|---|---|
| 权重 | 0, 1, 0(每个类名贡献一位权重)。 |
| 多类叠加 | 一个元素可以有多个类(空格分隔),各选择器独立作用并叠加效果。 |
| 链式写法 | .intro.highlight 匹配同时拥有两个类的元素(中间无空格),权重为 0, 2, 0。 |
| 命名规范 | 推荐使用小写字母与连字符(如 .main-header),避免使用下划线或驼峰以保持与 CSS 属性命名风格一致。 |
1.4 ID 选择器(ID Selector)
通过 HTML 元素的 id 属性值匹配。语法为 # 后跟 ID 名。
1 | <div id="main-content">核心内容区域</div> |
1 | #main-content { |
| 特性 | 说明 |
|---|---|
| 权重 | 1, 0, 0(极高,仅次于内联样式)。 |
| 唯一性要求 | HTML 规范要求 id 值在文档内唯一。即使浏览器容错允许重复 ID,JavaScript getElementById 也只会返回第一个。 |
| 适用场景 | 锚点导航目标(href="#main-content")、表单控件关联(<label for="user-id">)、JavaScript 定位。 |
| 样式使用建议 | 避免在 CSS 中使用 ID 选择器。高权重使其难以被覆盖,破坏层叠的灵活性。优先使用类选择器。 |
2. 组合选择器:基于 DOM 关系选择元素
组合选择器通过元素之间的层级关系(祖先-后代、父-子、相邻兄弟、后续兄弟)来定位目标。
2.1 后代选择器(Descendant Combinator)
语法:A B(A 与 B 之间以空格分隔)。匹配A 元素内部任意层级的所有 B 元素。
1 | <article> |
1 | /* 匹配 article 内所有层级下的 p 元素 */ |
2.2 子选择器(Child Combinator)
语法:A > B。匹配A 元素的直接子元素中的 B,不包含更深层的后代。
1 | /* 仅匹配 article 的直接子元素 p,不匹配嵌套在 div 内的 p */ |
以上例 HTML 结构为例,仅“直接子段落。”变粗体,“嵌套在 div 内的段落”不受影响。
2.3 相邻兄弟选择器(Adjacent Sibling Combinator)
语法:A + B。匹配紧跟在 A 之后的第一个兄弟元素 B(两者同父级)。
1 | <h2>标题</h2> |
1 | h2 + p { |
典型场景:当标题后紧跟段落时,移除段落的顶部边距,使标题与内容紧贴。
2.4 后续兄弟选择器(General Sibling Combinator)
语法:A ~ B。匹配 A 元素之后所有的兄弟元素 B(不要求紧邻,但必须在 A 之后)。
1 | h2 ~ p { |
上例 HTML 中,两个 <p> 都会被选中(因为它们都在 <h2> 之后)。
2.5 组合选择器权重计算
组合选择器的权重 = 各基础选择器权重之和。
| 选择器 | 分解 | 权重 |
|---|---|---|
div p |
1 个元素 + 1 个元素 | 0, 0, 2 |
.card > .title |
1 个类 + 1 个类(> 不贡献权重) |
0, 2, 0 |
#nav li.active |
1 个 ID + 1 个元素 + 1 个类 | 1, 1, 1 |
3. 属性选择器:基于属性及其值进行匹配
属性选择器允许你根据元素是否存在某个属性或属性的值来定位元素。语法用方括号 [] 包裹。
3.1 存在性匹配:[attr]
匹配所有拥有该属性的元素,无论值是什么。
1 | /* 所有设置了 title 属性的元素 */ |
3.2 精确值匹配:[attr="value"]
匹配属性值完全等于指定字符串的元素。
1 | /* 匹配 type 属性值为 "submit" 的 input */ |
3.3 包含值匹配的四种变体
| 语法 | 匹配规则 | 示例与应用场景 |
|---|---|---|
[attr~="value"] |
属性值包含完整单词 value(以空格分隔的值列表中的一个)。 |
[class~="btn"] 匹配 class="btn primary",不匹配 class="btn-primary"。 |
[attr|="value"] |
属性值精确等于 value 或以 value- 开头(常用于语言子标签匹配)。 |
[lang|="en"] 匹配 lang="en" 和 lang="en-US",不匹配 lang="enUS"。 |
[attr^="value"] |
属性值以 value 开头。 |
a[href^="https"] 匹配所有 HTTPS 外部链接。 |
[attr$="value"] |
属性值以 value 结尾。 |
a[href$=".pdf"] 匹配所有 PDF 文件链接(可配合添加 PDF 图标)。 |
[attr*="value"] |
属性值**包含子串 value**(在任意位置)。 |
a[href*="example"] 匹配所有包含 “example” 的链接。 |
3.4 大小写敏感性修饰
默认情况下,属性选择器是大小写敏感的。可在闭合方括号前加 i 或 s 修饰符:
i:大小写不敏感(case-insensitive)。s:大小写敏感(case-sensitive,默认行为,显式声明)。
1 | /* 匹配 type="submit"、type="Submit"、type="SUBMIT" 等 */ |
4. 伪类选择器:基于状态与位置的选择
伪类选择器以 : 开头,用于选择处于特定状态或特定位置的元素。它们不修改 DOM,而是根据浏览器状态或元素结构动态匹配。
4.1 动态交互伪类
这些伪类响应用户与元素的交互。
| 伪类 | 匹配状态 | 典型场景 |
|---|---|---|
:hover |
鼠标指针悬停在元素上方时。 | 链接变色、按钮效果、显示隐藏元素。 |
:focus |
元素获得焦点时(无论是鼠标点击还是键盘 Tab)。 | 输入框边框高亮、表单控件提示。 |
:focus-visible |
仅当通过键盘(或非鼠标)方式获得焦点时匹配。 | 为键盘用户保留清晰焦点轮廓,鼠标点击时不显示。 |
:active |
元素被激活时(通常指鼠标按下到释放的瞬间)。 | 按钮按下的视觉反馈。 |
:visited |
用户已访问过的链接。 | 区分已访问和未访问的链接颜色。 |
注意顺序:对于链接,推荐顺序为 :link — :visited — :hover — :active(LVHA 顺序),以确保各状态样式正确层叠。
1 | a:link { |
4.2 结构化位置伪类
根据元素在 DOM 树中的位置进行选择。
| 伪类 | 匹配规则 |
|---|---|
:first-child |
是其父元素的第一个子元素。 |
:last-child |
是其父元素的最后一个子元素。 |
:nth-child(n) |
是其父元素的第 n 个子元素。n 从 1 开始,支持 odd/even/2n+1 等公式。 |
:nth-last-child(n) |
是其父元素的倒数第 n 个子元素。 |
:first-of-type |
是其父元素中该类型的第一个元素。 |
:last-of-type |
是其父元素中该类型的最后一个元素。 |
:nth-of-type(n) |
是其父元素中该类型的第 n 个元素。 |
:only-child |
是其父元素的唯一子元素。 |
:only-of-type |
是其父元素中该类型的唯一元素。 |
nth-child 与 nth-of-type 的核心区别:
1 | <div> |
1 | /* 匹配父元素中第二个子元素——<span>(因为 span 是第二个子元素) */ |
n 的公式用法:
2n/even:偶数项。2n+1/odd:奇数项。3n+1:每 3 项的第 1 项(1, 4, 7…)。
4.3 表单状态伪类
专门针对表单控件的各种状态。
| 伪类 | 匹配状态 |
|---|---|
:checked |
被选中的单选按钮或复选框(<input type="radio/checkbox">)、<option>。 |
:disabled |
被禁用的表单控件。 |
:enabled |
未被禁用的表单控件(<input> 默认状态)。 |
:required |
带有 required 属性的表单控件。 |
:optional |
不带有 required 属性的表单控件。 |
:valid |
输入值通过浏览器验证的控件。 |
:invalid |
输入值未通过浏览器验证的控件。 |
:in-range |
输入值在 min 和 max 范围内(type="number/date" 等)。 |
:out-of-range |
输入值超出 min 和 max 范围。 |
:placeholder-shown |
输入框的 placeholder 文本正显示时(即输入框为空且有 placeholder)。 |
1 | /* 被选中的复选框后紧跟的 label 变绿 */ |
4.4 其他常用伪类
| 伪类 | 匹配规则 | 场景 |
|---|---|---|
:not(selector) |
匹配不匹配括号内选择器的元素。 | 为除最后一个外的所有项添加边距。 |
:empty |
没有任何子节点(包括文本节点)的元素。 | 隐藏空内容容器。 |
:root |
文档的根元素(通常为 <html>)。 |
声明 CSS 自定义属性(变量)的作用域。 |
:target |
URL 哈希值指向的元素(如 #section1)。 |
锚点跳转后的高亮效果。 |
1 | /* 为导航栏中除最后一个外的所有 <li> 添加右边距 */ |
课后练习
一、概念自测(选择题 / 填空题)
(单选) 以下哪个选择器的权重最高?
A..nav .item
B.#header
C.ul > li.active
D.[type="submit"](单选)
p:nth-child(3)和p:nth-of-type(3)的区别是?
A. 两者完全等价,无任何区别。
B.nth-child匹配父元素第三个子元素且该子元素必须是p;nth-of-type匹配父元素中第三个p子元素。
C.nth-child匹配父元素中第三个p子元素;nth-of-type匹配父元素第三个子元素且必须是p。
D.nth-child仅对<p>生效,nth-of-type对所有元素生效。(填空) 要匹配所有以
https开头的外部链接,应使用属性选择器a[href______="https"]。(多选) 以下伪类中,哪些属于动态交互伪类?
A.:hover
B.:first-child
C.:focus
D.:checked
二、AI 编程任务:编写面向 AI 的提示词
场景:你需要为一个表格实现斑马纹(交替行颜色) 和悬停高亮效果,同时为“状态”列中的不同状态文字添加视觉标识。要求如下:
- 表格偶数行背景色为
#f2f2f2,奇数行白色。 - 鼠标悬停某一行时,该行背景色变为
#e0e7ff。 - “状态”列(假设对应
<td>带有class="status")中,若文字为“已完成”,颜色为绿色;若为“进行中”,颜色为橙色;若为“已取消”,颜色为灰色且文字加删除线。 - 表格最后一行(汇总行)文字加粗,且上边框为双线。
- 使用纯 CSS 实现,不添加额外 HTML 元素或类名(除已有的
.status外,其余通过结构伪类和属性选择器实现)。
任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成符合上述要求的 HTML 表格结构与完整 CSS。提示词中应明确指定使用哪些伪类和选择器,以及各状态的颜色规则。
三、面试真题与参考答案
题目(百度前端面试题):
请详细说明 CSS 中
:nth-child()和:nth-of-type()的区别。给出一个具体的 HTML 结构示例,并解释p:nth-child(2)和p:nth-of-type(2)分别会选中哪些元素,为什么?
参考答案:
1. 核心区别:
| 伪类 | 匹配逻辑 |
|---|---|
:nth-child(n) |
首先找到父元素中第 n 个子元素(不论类型),然后检查该子元素是否匹配前面的选择器。若类型不匹配,则不选中任何元素。 |
:nth-of-type(n) |
首先筛选出父元素中所有与前面选择器同类型的元素,然后在这些同类型兄弟元素中选择第 n 个。 |
2. 具体示例分析:
1 | <div> |
p:nth-child(2)的匹配过程:- 查找父元素
<div>的第 2 个子元素 → 是<p>段落 1</p>。 - 检查该子元素是否为
<p>→ 是。 - 结果:选中“段落 1”。
- 查找父元素
p:nth-of-type(2)的匹配过程:- 筛选
<div>中所有<p>元素 →[<p>段落 1</p>, <p>段落 2</p>]。 - 从该列表中选取第 2 个 →
<p>段落 2</p>。 - 结果:选中“段落 2”。
- 筛选
3. 如果 p:nth-child(1) 会发生什么?
- 父元素第 1 个子元素是
<span>,不是<p>,因此什么都不选中(即使后面有<p>存在)。
4. 适用场景总结:
- 当确保同类型元素连续且没有其他类型的兄弟元素插入时,两者效果等价。
- 当 HTML 结构中混有多种类型的兄弟元素(如标题、图片、段落交错)时,必须根据具体意图谨慎选择:是要“第 n 个子元素且恰好是某类型”(
nth-child),还是“某类型中的第 n 个”(nth-of-type)。
课后练习答案
一、概念自测答案
B
- 解析:ID 选择器权重为 (1, 0, 0),高于类选择器和元素选择器的任意组合。A 为 (0, 2, 0);C 为 (0, 1, 2);D 为 (0, 1, 1)。
B
- 解析:
nth-child先定位父元素中第 n 个子元素,再检查类型;nth-of-type先筛选同类型元素,再定位第 n 个。
- 解析:
**
^=**(或^后跟=)- 解析:
[attr^="value"]表示匹配属性值以value开头的元素。
- 解析:
A、C
- 解析:
:hover和:focus是响应用户交互的动态伪类。:first-child是结构伪类;:checked是表单状态伪类。
- 解析:
二、AI 编程任务参考答案(提示词示例)
示例提示词:
“请生成一个包含完整 CSS 的 HTML 表格,实现以下样式:
- 表格有表头和至少 5 行数据,最后一行为汇总行。
- 使用
tr:nth-child(even)实现偶数行背景色#f2f2f2,奇数行白色。- 使用
tr:hover实现悬停行背景色#e0e7ff。- 状态列单元格有类名
.status。使用属性选择器或伪类匹配不同状态文字(不使用额外类名):若title属性为已完成则文字绿色;若data-status属性为进行中则文字橙色;若文本内容为已取消则文字灰色且加删除线(text-decoration: line-through)。- 使用
tr:last-child td为最后一行所有单元格设置font-weight: bold和border-top: 3px double #333。- 代码整洁,注释清晰。直接输出完整 HTML 文件。”
三、面试题答案
(答案详见上文“参考答案”部分)