CSS 选择器总结
Category(分类): CSS Status: 已整理
CSS 选择器用于从文档中匹配元素,并为匹配到的元素应用样式。常见选择器可以分为:
- 基础选择器;
- 属性选择器;
- 组合选择器;
- 伪类选择器;
- 伪元素选择器。
选择器匹配到元素后,最终是否生效还要受到层叠、选择器优先级、继承和样式声明顺序影响。
一、基础选择器
1. ID 选择器
ID 选择器使用 # 加上 ID 名称,匹配具有对应 id 属性的元素:
<div id="page">我是 ID 选择器</div>
#page {
color: #fff;
background: #000;
}
同一个页面中,id 通常应保持唯一。ID 选择器优先级较高,不建议把它作为组件样式的主要选择方式,否则后续覆盖样式会比较困难。
2. 类选择器
类选择器使用 . 加上 class 名称,匹配具有对应类名的元素:
<ul>
<li class="list-item">1</li>
<li class="list-item">2</li>
<li class="list-item">3</li>
</ul>
.list-item {
color: red;
font-size: 16px;
}
类选择器可以复用,也可以给同一元素添加多个类:
<div class="card active"></div>
.card.active {
border-color: #168bf5;
}
3. 通配符选择器
通配符选择器使用 *,可以匹配范围内的所有元素:
* {
margin: 0;
padding: 0;
}
现代项目中更常见的基础重置写法是:
*,
*::before,
*::after {
box-sizing: border-box;
}
通配符选择器的主要问题是影响范围很广,可能带来意外样式和维护成本。不能简单断言它一定“性能很差”,现代浏览器会对选择器进行优化,实际影响还取决于 DOM 规模和样式变化频率。
4. 标签选择器
标签选择器直接使用 HTML 标签名:
li {
font-size: 20px;
}
它适合设置元素的基础样式或重置样式,例如:
h1,
h2,
p {
margin: 0;
}
标签选择器会匹配页面中所有相同类型的元素,因此组件样式中通常更推荐使用类选择器进行限制:
.article-list li {
margin-block: 0.5rem;
}
二、属性选择器
属性选择器使用方括号 [] 根据元素属性进行匹配。属性值建议使用引号,以提高可读性和兼容不同取值的能力。
1. [attr]:具有某个属性
a[href] {
color: red;
}
它会匹配具有 href 属性的 <a> 元素,不要求属性值是什么。
2. [attr="value"]:属性值完全相等
<input type="text" value="xuanze">
input[type="text"] {
color: red;
}
完全匹配属性值:
<input class="input text" type="text" value="xuanze">
input[class="input text"] {
color: red;
}
上面的写法要求整个 class 属性值完全一致,以下情况都不会匹配:
<input class="text input" type="text">
<input class="input text large" type="text">
如果想匹配 class 中的一个独立单词,应使用 [class~="input"]。
3. [attr~="value"]:空格分隔的单词匹配
<input class="input text">
input[class~="input"] {
color: red;
}
它匹配以空格分隔的独立单词 input,但不会匹配:
<input class="input-text">
4. [attr|="value"]:完全相等或以 value- 开头
<div class="article"></div>
<div class="article-title"></div>
<div class="article-content"></div>
<div class="article_footer"></div>
div[class|="article"] {
color: red;
}
会匹配 article、article-title 和 article-content,不会匹配 article_footer。注意正确语法是 |=,不是 |。
5. [attr^="value"]:属性值以指定内容开头
div[class^="article"] {
color: red;
}
会匹配以 article 开头的属性值,例如 article-title 和 article_footer。
它不是正则表达式,而是 CSS 定义的字符串前缀匹配。如果写成 artice,由于拼写错误,将匹配不到 article 开头的值。
6. [attr$="value"]:属性值以指定内容结尾
a[href$=".pdf"] {
padding-right: 1.2em;
}
上面会匹配 href 以 .pdf 结尾的链接。
7. [attr*="value"]:属性值包含指定字符串
[class*="icon"] {
display: inline-block;
}
它匹配属性值中包含 icon 子字符串的元素,不要求它是空格分隔的独立单词。
8. 属性值匹配的大小写标志
可以在属性值后增加 i,进行 ASCII 范围内的大小写不敏感匹配:
input[type="TEXT" i] {
color: red;
}
默认是否区分大小写还会受到 HTML 属性定义和文档语言规则影响,不能对所有属性一概而论。
三、组合选择器
1. 后代选择器
后代选择器使用空格分隔多个选择器,匹配前一个元素内部任意层级的后代:
<div class="page">
<div class="article">
<p>文章内容</p>
</div>
<p>页面内容</p>
</div>
.page p {
color: gold;
font-size: 20px;
}
.page p 会匹配两个 <p>,因为它们都是 .page 的后代。
2. 子元素选择器
子元素选择器使用 >,只匹配直接子元素:
.page > p {
color: red;
}
上面的规则只匹配 .page 的直接 <p>,不会匹配嵌套在 .article 内部的 <p>。
3. 相邻兄弟选择器
相邻兄弟选择器使用 +,匹配紧跟在前一个元素后面的同级兄弟元素:
h1 + p {
margin-top: 20px;
color: black;
}
只有 h1 后面紧邻的第一个符合条件的 <p> 会被匹配,中间不能有其他元素。
4. 通用兄弟选择器
通用兄弟选择器使用 ~,匹配前一个元素之后的所有符合条件的同级兄弟元素:
h1 ~ p {
color: red;
}
它只匹配后面的兄弟,不匹配前面的元素,也不匹配嵌套后代。
5. 交集选择器
交集选择器把多个简单选择器连续写在一起,中间不能有空格:
.list-item.active {
color: red;
font-size: 20px;
}
它匹配同时具有 list-item 和 active 两个类的元素。
以下写法含义不同:
.list-item .active {
/* 匹配 .list-item 内部的 .active 后代 */
}
6. 并集选择器
并集选择器使用逗号分隔多个选择器,让多个选择器共享同一组声明:
h1,
h2,
p {
margin: 0;
padding: 0;
}
逗号分隔的每一项都是一个独立选择器,某一项语法错误时可能影响整个规则,因此建议分行书写。
四、伪类选择器
伪类使用一个冒号 :,用于根据元素状态、位置或结构条件进行匹配。
1. 链接和交互状态
:link {
/* 未访问的链接 */
}
:visited {
/* 已访问的链接 */
}
:hover {
/* 指针悬停或其他指针交互状态 */
}
:active {
/* 元素处于激活过程,例如按下按钮但尚未释放 */
}
:focus {
/* 元素获得焦点 */
}
:focus-visible {
/* 浏览器判断应显示明显焦点指示时 */
}
:link 和 :visited 主要用于链接。:visited 出于隐私保护只能应用有限的样式属性。
:active 不是“点击后一直保持选中”,通常只在激活交互期间生效。键盘可访问性场景应保留清晰的 :focus 或 :focus-visible 样式。
链接样式经常按照以下顺序书写,以减少相互覆盖:
/* link, visited, focus, hover, active */
a:link {
color: #168bf5;
}
a:visited {
color: #7048e8;
}
a:focus-visible {
outline: 2px solid currentColor;
}
a:hover {
text-decoration: underline;
}
a:active {
color: #d6336c;
}
2. 表单状态
input:checked {
accent-color: #168bf5;
}
input:disabled {
cursor: not-allowed;
opacity: 0.6;
}
input:enabled {
cursor: text;
}
option:checked {
font-weight: bold;
}
:checked 不只适用于 radio 和 checkbox,也可以匹配被选中的 <option>。不同表单控件对可应用样式的支持可能有所差异。
3. 结构伪类
:empty
匹配没有子节点的元素:
.box:empty {
display: none;
}
空白文本节点也可能使元素不再符合 :empty,因此 HTML 中的换行和空格需要注意。
:first-child 和 :last-child
li:first-child {
color: red;
}
li:last-child {
color: blue;
}
它们判断的是元素在父元素所有子元素中的位置,而不是同类型元素中的位置。
<div>
<p>第一个子元素</p>
<li>第二个子元素</li>
</div>
此时 li:first-child 不会匹配 <li>,因为它不是父元素的第一个子元素。
如果想匹配同类型元素中的第一个或最后一个,应使用:
li:first-of-type {
color: red;
}
li:last-of-type {
color: blue;
}
:nth-child(an + b)
li:nth-child(2n + 1) {
background: #f5f5f5;
}
这会匹配父元素中位置为 1、3、5、7... 的子元素,但位置计算包含所有子元素,不是只统计 li。
li:nth-last-child(2n + 1) {
background: #f5f5f5;
}
:nth-last-child() 从父元素末尾开始计算。
如果需要按同类型元素计算,应使用:
li:nth-of-type(2n + 1) {
background: #f5f5f5;
}
:only-child 和 :only-of-type
.item:only-child {
border: 1px solid red;
}
p:only-of-type {
color: green;
}
:only-child:元素是父元素唯一的子元素;:only-of-type:元素是父元素中唯一的同类型元素。
4. 现代常用伪类
:not()
排除满足条件的元素:
.button:not(.disabled) {
cursor: pointer;
}
:is()
将多个选择器组合为一个匹配条件:
:is(h1, h2, h3) {
line-height: 1.2;
}
:where()
功能类似 :is(),但其特异性为零,适合编写容易被覆盖的基础样式:
:where(.card, .panel) {
border-radius: 8px;
}
:has()
根据后代或相邻关系选择元素:
.card:has(img) {
padding-top: 0;
}
使用前应确认目标浏览器支持情况,并注意复杂选择器可能增加维护成本。
其他常见伪类还包括:
:root /* 文档根元素 */
:target /* URL 片段指向的目标元素 */
:required /* 必填表单控件 */
:valid /* 通过约束验证的表单控件 */
:invalid /* 未通过约束验证的表单控件 */
五、伪元素选择器
伪元素使用两个冒号 ::,用于选择元素的某个虚拟部分或生成内容。伪元素不是独立的真实 DOM 节点。
1. ::first-line
设置块容器第一行的样式:
.article::first-line {
font-weight: bold;
}
第一行会随着容器宽度、字体和内容变化而变化,因此不能把它理解为固定的文本节点。
2. ::first-letter
设置文本开头的首个排版字符:
.article::first-letter {
float: left;
margin-right: 0.25em;
font-size: 2.5em;
line-height: 1;
}
::first-line 和 ::first-letter 有适用的布局条件,主要用于块容器等适合的盒子,不是所有行内元素都能使用。
3. ::before 和 ::after
它们可以在元素内容的前后生成伪元素:
.badge::before {
content: '提示:';
color: #d6336c;
}
.badge::after {
content: '';
display: block;
clear: both;
}
::before 和 ::after 通常需要设置 content 才会生成。生成的内容属于 CSS 呈现结果,不会作为真实 DOM 节点出现在 HTML 结构中。
JavaScript 不能直接使用 querySelector('::before') 查询它们,但可以通过修改宿主元素的类名、CSS 变量或内联样式间接控制:
.button::before {
content: var(--label, '提交');
}
button.style.setProperty('--label', '"加载中"')
4. ::selection
设置用户选中的文本样式:
::selection {
color: #fff;
background: #168bf5;
}
它针对的是文本选区,不是整个元素,并且可用 CSS 属性受到浏览器限制。
5. 单冒号和双冒号
现代 CSS 推荐:
- 伪类使用一个冒号,例如
:hover; - 伪元素使用两个冒号,例如
::before。
部分早期伪元素写法,如 :before 和 :after,仍可能得到浏览器兼容支持,但新代码建议使用双冒号。
六、选择器优先级
当多个规则同时匹配同一个元素时,需要根据层叠和选择器特异性决定最终样式。常见特异性由高到低大致为:
!important声明(还要结合来源和层叠层判断);- 行内样式;
- ID 选择器;
- 类选择器、属性选择器、伪类;
- 标签选择器、伪元素;
- 通配符和继承样式。
例如:
#page .title {
color: red;
}
.page .title {
color: blue;
}
#page .title 包含一个 ID,因此通常优先级高于 .page .title。
!important 不应作为常规解决冲突的手段。更好的做法是降低选择器嵌套、整理组件边界和调整层叠顺序。
需要特别注意:
:where()的特异性为零;:is()、:not()、:has()的特异性取参数列表中最高的那一项;- 选择器特异性相同时,后声明的规则通常覆盖先声明的规则;
- 特异性只在相同层叠来源和层叠层条件下比较。
七、选择器的维护建议
- 组件样式优先使用语义清晰的类选择器;
- 不要过度依赖 ID 选择器;
- 避免过深的后代选择器,例如
.page .content .list ul li span; - 使用
>、+、~明确表达父子和兄弟关系; - 属性选择器适合状态和语义属性匹配,不要把复杂业务逻辑全部塞进属性字符串;
- 使用
:where()编写低特异性的基础样式; - 使用
:focus-visible保留键盘用户可见的焦点状态; - 不要为了覆盖样式而滥用
!important; - 选择器性能不要只看单个符号,要结合 DOM 规模、动态更新频率和可维护性判断;
- 使用现代伪类前确认项目的浏览器支持范围。
总结
#id选择器匹配 ID,类选择器匹配 class,标签选择器匹配元素类型;*的影响范围最广,常用于基础重置,但要注意维护范围;- 属性选择器可以进行存在、完全相等、空格单词、连字符前缀、前缀、后缀和子字符串匹配;
^=、$=、*=不是正则表达式;- 空格表示后代,
>表示直接子元素,+表示相邻兄弟,~表示后续所有兄弟; - 多个简单选择器连续书写表示交集,逗号表示并集;
:first-child判断所有子元素的位置,:first-of-type判断同类型元素的位置;:nth-child()的位置计算包括所有子元素;:active表示激活过程,:focus和:focus-visible表示焦点状态;- 伪元素不是真实 DOM 节点,
::before和::after通常需要content; - 现代项目还应掌握
:not()、:is()、:where()、:has()等伪类; - 选择器最终效果还会受到特异性、层叠顺序、继承和声明顺序影响。