深入理解 CSS 选择器完整体系
所有选择器分类、优先级完整计算、:has/:is/:where 差异、@layer 对级联的影响
什么是 CSS 选择器?
定义:CSS 选择器(Selector)是用于匹配 HTML 元素的模式表达式。它告诉浏览器"哪些元素应该应用这组样式规则"。选择器的能力直接决定了样式能否精准地作用于目标元素,是 CSS 的基础中的基础。
涉及场景:
- 组件样式隔离:通过 BEM 命名、属性选择器、
:where()等精确控制样式作用范围 - 条件样式:
:has()实现"父元素选择器",根据子元素状态改变父元素样式 - 表单状态:
:invalid、:placeholder-shown、:user-valid实现表单验证样式 - 主题切换:
:root自定义属性 +:is()批量匹配简化主题代码 - 打印 / 无障碍:
:focus-visible、@media print配合选择器优化可访问性 - CSS 架构:
@layer级联层控制第三方库与项目样式的优先级关系
作用:
- 精准定位元素:从简单的类选择器到复杂的组合选择器,覆盖一切匹配需求
- 控制优先级:理解特异性(specificity)计算规则,避免
!important滥用 - 减少 JS 依赖:
:has()、:is()、:where()等新选择器让纯 CSS 能处理更多交互逻辑 - 面试必考:选择器优先级计算、
:is()与:where()的区别是高频考题
选择器分类
基础选择器
css
* { } /* 通配符选择器 */
div { } /* 元素(类型)选择器 */
.class { } /* 类选择器 */
#id { } /* ID选择器 */
[attr] { } /* 属性选择器 */属性选择器(完整)
css
[attr] { } /* 有该属性 */
[attr="val"] { } /* 属性值完全等于 val */
[attr~="val"] { } /* 属性值是空格分隔的列表,其中一个等于 val */
[attr|="val"] { } /* 属性值等于 val 或以 val- 开头(常用于 lang) */
[attr^="val"] { } /* 属性值以 val 开头 */
[attr$="val"] { } /* 属性值以 val 结尾 */
[attr*="val"] { } /* 属性值包含 val */
[attr="val" i] { } /* 不区分大小写(i 修饰符) */
[attr="val" s] { } /* 区分大小写(s 修饰符,默认) */
/* 实用示例 */
a[href^="https"] { color: green; } /* 外部链接 */
a[href$=".pdf"] { } /* PDF链接 */
img[src*="avatar"] { border-radius: 50%; } /* 头像图片 */
[data-theme="dark"] { } /* 自定义数据属性 */
input[type="email" i] { } /* 不区分大小写匹配 */组合选择器
css
A B { } /* 后代选择器(B 是 A 的后代,任意层级) */
A > B { } /* 子选择器(B 是 A 的直接子元素) */
A + B { } /* 相邻兄弟(B 紧跟 A 之后) */
A ~ B { } /* 通用兄弟(B 在 A 之后的任意兄弟) */
A, B { } /* 选择器列表(A 或 B) */
/* 列组合器(表格列选择,实验性) */
col || td { } /* 属于该列的单元格 */伪类选择器
css
/* 状态伪类 */
:hover { } /* 鼠标悬停 */
:active { } /* 激活(鼠标按下) */
:focus { } /* 获得焦点 */
:focus-visible { } /* 键盘焦点(不是鼠标点击焦点) */
:focus-within { } /* 自身或后代获得焦点 */
:visited { } /* 已访问链接 */
:link { } /* 未访问链接 */
:target { } /* URL 锚点匹配的元素 */
:checked { } /* 选中的 checkbox/radio/option */
:indeterminate { } /* 不确定状态的 checkbox */
:disabled { } /* 禁用的表单元素 */
:enabled { } /* 启用的表单元素 */
:required { } /* 必填字段 */
:optional { } /* 可选字段 */
:valid { } /* 验证通过的表单元素 */
:invalid { } /* 验证失败的表单元素 */
:in-range { } /* 值在 min/max 范围内 */
:out-of-range { } /* 值超出 min/max 范围 */
:placeholder-shown { } /* 显示 placeholder 的输入框 */
:read-only { } /* 只读元素 */
:read-write { } /* 可编辑元素 */
:default { } /* 默认的表单元素(如默认按钮) */
:defined { } /* 已定义的元素(用于自定义元素) */
:user-valid { } /* 用户交互后验证通过(新) */
:user-invalid { } /* 用户交互后验证失败(新) */
/* 结构伪类 */
:first-child { } /* 第一个子元素 */
:last-child { } /* 最后一个子元素 */
:nth-child(n) { } /* 第n个子元素 */
:nth-last-child(n) { } /* 倒数第n个子元素 */
:only-child { } /* 唯一子元素 */
:first-of-type { } /* 同类型中的第一个 */
:last-of-type { } /* 同类型中的最后一个 */
:nth-of-type(n) { } /* 同类型中的第n个 */
:nth-last-of-type(n) { } /* 同类型中倒数第n个 */
:only-of-type { } /* 同类型中唯一的 */
:empty { } /* 没有子元素的元素(包括文本) */
:root { } /* 根元素(通常是 <html>) */
/* nth 公式 */
:nth-child(2n) { } /* 偶数(等价于 even) */
:nth-child(2n+1) { } /* 奇数(等价于 odd) */
:nth-child(3n) { } /* 每3个 */
:nth-child(n+4) { } /* 从第4个开始 */
:nth-child(-n+3) { } /* 前3个 */
:nth-child(n+3):nth-child(-n+8) { } /* 第3到第8个 */
/* 逻辑伪类 */
:not(selector) { } /* 否定 */
:is(selector) { } /* 匹配列表中的任一选择器 */
:where(selector) { } /* 同 :is,但优先级为0 */
:has(selector) { } /* 包含匹配的子元素(父选择器!) */
/* 其他伪类 */
:lang(zh) { } /* 语言匹配 */
:dir(ltr) { } /* 文本方向 */
:fullscreen { } /* 全屏模式 */
:modal { } /* 模态状态的 dialog */
:popover-open { } /* 打开的 popover */
:playing { } /* 正在播放的媒体 */
:paused { } /* 暂停的媒体 */伪元素选择器
css
::before { } /* 元素前面插入内容 */
::after { } /* 元素后面插入内容 */
::first-line { } /* 第一行文本 */
::first-letter { } /* 第一个字母 */
::selection { } /* 被选中的文本 */
::placeholder { } /* 输入框占位符 */
::marker { } /* 列表项标记 */
::backdrop { } /* dialog/fullscreen 的背景层 */
::file-selector-button { } /* 文件选择按钮 */
::highlight(name) { } /* 自定义高亮(Custom Highlight API) */
::view-transition-old(name) { } /* View Transition 旧视图 */
::view-transition-new(name) { } /* View Transition 新视图 */优先级(Specificity)计算
计算规则
优先级由三个分量组成 (A, B, C),从左到右比较:
A = ID 选择器的数量
B = 类选择器、属性选择器、伪类的数量
C = 元素选择器、伪元素的数量
通配符 *、组合器 (>, +, ~, 空格) 和 :where() 不影响优先级css
/* 优先级计算示例 */
* /* (0, 0, 0) */
div /* (0, 0, 1) */
div p /* (0, 0, 2) */
.class /* (0, 1, 0) */
div.class /* (0, 1, 1) */
#id /* (1, 0, 0) */
#id .class /* (1, 1, 0) */
#id .class div /* (1, 1, 1) */
div:hover /* (0, 1, 1) */
[type="text"] /* (0, 1, 0) */
div::before /* (0, 0, 2) */
#id .class[type] div:hover /* (1, 3, 1) */
/* 内联样式的优先级高于所有选择器 */
style="color: red" /* 相当于 (1, 0, 0, 0),多一个维度 */
/* !important 覆盖一切(包括内联) */
.class { color: red !important; }:is() vs :where() vs :not() vs :has() 的优先级
css
/* :is() —— 取参数中优先级最高的 */
:is(#id, .class, div) { }
/* 优先级 = (1, 0, 0)(取 #id 的优先级) */
/* :where() —— 优先级永远是 0 */
:where(#id, .class, div) { }
/* 优先级 = (0, 0, 0)(无论参数是什么) */
/* 适合写默认样式/重置样式,容易被覆盖 */
/* :not() —— 取参数的优先级 */
:not(.class) { }
/* 优先级 = (0, 1, 0)(.class 的优先级) */
:not(#id) { }
/* 优先级 = (1, 0, 0) */
/* :has() —— 取参数的优先级 */
div:has(> .active) { }
/* 优先级 = (0, 1, 1)(div + .active) */
/* 实际应用 */
/* :where() 写可被覆盖的基础样式 */
:where(.btn) {
padding: 8px 16px;
border: 1px solid;
}
/* 任何 .btn 样式都能覆盖上面的规则 */
/* :is() 简化选择器列表 */
:is(h1, h2, h3, h4, h5, h6) {
margin-top: 1.5em;
}
/* 等价于 h1, h2, h3, h4, h5, h6 { } */
/* 但 :is 的容错性更好——一个无效选择器不会使整条规则失效 */:has() 父选择器(游戏规则改变者)
css
/* :has() 可以根据子元素的状态选择父元素 */
/* 包含图片的卡片 */
.card:has(img) {
grid-template-rows: 200px 1fr;
}
/* 有 .error 子元素的表单组 */
.form-group:has(.error) {
border-color: red;
}
/* 有选中 checkbox 的行 */
tr:has(input:checked) {
background: #e6f7ff;
}
/* 没有段落的 article */
article:not(:has(p)) {
font-style: italic;
}
/* 相邻元素 + :has 实现前瞻 */
/* 后面紧跟 h2 的 h1 */
h1:has(+ h2) {
margin-bottom: 0;
}
/* 暗色模式切换(无 JS) */
:root:has(#dark-mode:checked) {
--bg: #1a1a1a;
--text: #eee;
}
/* 空状态 */
.list:has(:not(li)) {
display: none;
}
.empty-state {
display: none;
}
.list:not(:has(li)) + .empty-state {
display: block;
}@layer 级联层
CSS 级联层(Cascade Layers)改变了样式优先级的规则:
css
/* 定义层的顺序(后定义的优先级更高) */
@layer reset, base, components, utilities;
/* 在层中定义样式 */
@layer reset {
* { margin: 0; padding: 0; box-sizing: border-box; }
}
@layer base {
body { font-family: sans-serif; line-height: 1.6; }
a { color: blue; }
}
@layer components {
.btn { padding: 8px 16px; border: 1px solid; border-radius: 4px; }
.card { border: 1px solid #ddd; border-radius: 8px; }
}
@layer utilities {
.text-center { text-align: center; }
.mt-4 { margin-top: 1rem; }
}
/* 优先级:reset < base < components < utilities */
/* 无论选择器的优先级如何! */
/* 未分层的样式优先级高于所有层 */
.special { color: red; } /* 优先级高于任何 @layer 中的样式 */
/* 导入时指定层 */
@import url('reset.css') layer(reset);
@import url('bootstrap.css') layer(framework);
/* 嵌套层 */
@layer components {
@layer button {
.btn { }
}
@layer card {
.card { }
}
}
/* 引用嵌套层:components.button */
/* 级联优先级(从低到高) */
/*
1. 浏览器默认样式
2. @layer 中的普通样式(按层顺序)
3. 未分层的普通样式
4. 未分层的 !important 样式
5. @layer 中的 !important 样式(层顺序反转!)
6. 浏览器默认 !important 样式
*/完整级联优先级
从低到高:
1. 来源:用户代理(浏览器默认)< 用户 < 作者
2. @layer 层顺序(先定义的优先级低)
3. 未分层样式 > 所有 @layer 样式
4. 选择器优先级 (A, B, C)
5. 源码顺序(后出现的覆盖先出现的)
6. 内联样式 > 所有选择器
7. !important 反转以上所有规则
完整优先级链(从低到高):
用户代理普通 → 用户普通 → @layer普通(按层序) → 作者普通(未分层)
→ 动画 @keyframes
→ 作者!important(未分层) → @layer!important(层序反转) → 用户!important → 用户代理!important
→ transition性能考量
css
/* 浏览器从右到左匹配选择器 */
/* ❌ 低效:先匹配所有 a,再向上查找 */
body > div > ul > li > a { }
/* ✅ 高效:直接匹配类名 */
.nav-link { }
/* ❌ 避免使用通配符后代选择器 */
.header * { }
/* ❌ 避免过度嵌套 */
.page .container .sidebar .nav .item .link { }
/* ✅ 使用扁平的类名 */
.sidebar-nav-link { }
/* 选择器性能排序(从快到慢) */
/* #id > .class > tag > * > 后代 > 子 > 兄弟 > 属性 > 伪类 */总结
CSS 选择器核心知识点:
┌──────────────────────────────────────────────────────────┐
│ 优先级计算 │
│ • (A, B, C) = (ID, 类/属性/伪类, 元素/伪元素) │
│ • !important > 内联 > ID > 类 > 元素 │
│ • :is()/:not()/:has() 取参数的优先级 │
│ • :where() 优先级永远为 0 │
├──────────────────────────────────────────────────────────┤
│ 新选择器 │
│ • :has() 父选择器——根据子元素状态选择父元素 │
│ • :is() 选择器列表简化 + 容错 │
│ • :where() 零优先级版本的 :is() │
│ • :focus-visible 键盘焦点样式 │
│ • :user-valid/:user-invalid 用户交互后的验证状态 │
├──────────────────────────────────────────────────────────┤
│ @layer 级联层 │
│ • 改变级联优先级规则,层序 > 选择器优先级 │
│ • 未分层样式 > 所有层内样式 │
│ • !important 时层顺序反转 │
│ • 适合组织第三方库 vs 自定义样式 │
└──────────────────────────────────────────────────────────┘