Skip to content

深入理解 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 级联层控制第三方库与项目样式的优先级关系

作用

  1. 精准定位元素:从简单的类选择器到复杂的组合选择器,覆盖一切匹配需求
  2. 控制优先级:理解特异性(specificity)计算规则,避免 !important 滥用
  3. 减少 JS 依赖:has():is():where() 等新选择器让纯 CSS 能处理更多交互逻辑
  4. 面试必考:选择器优先级计算、: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 自定义样式                              │
└──────────────────────────────────────────────────────────┘