写CSS时,真正决定“这段样式应该应用到哪个元素”的并不是color、font-size或者display,而是规则最前面的选择器。
例如同一个网页中可能有几十个链接,如果只想修改导航栏里的链接、某个按钮里的链接或者带有特定属性的链接,就需要使用不同的CSS选择器进行定位。
CSS选择器英文通常写作CSS Selector。按照W3C当前Selectors规范的定义,选择器本质上是一种用于匹配文档树中元素的模式。除了CSS样式本身,JavaScript中的querySelector()和querySelectorAll()也使用类似的选择器语法,因此理解选择器不仅是学习CSS的基础,也会直接影响DOM操作和网页调试。

CSS选择器是怎么工作的
一个最普通的CSS规则可以写成:
p {
color: #333;
}其中p就是选择器,它告诉浏览器:找到页面中的所有<p>元素,然后应用后面的样式声明。
如果HTML是:
<p>第一段文字</p> <p>第二段文字</p>
两个段落都会被匹配。
但真实网站很少只需要按照标签统一设置样式。随着页面结构变复杂,还需要根据class、id、属性、元素层级和当前状态继续缩小匹配范围。
最基础的几种选择器
日常开发中最先接触的通常是元素选择器、Class选择器、ID选择器和通配选择器。
| 选择器 | 示例 | 作用 |
|---|---|---|
| 元素选择器 | p | 匹配所有p元素 |
| Class选择器 | .article | 匹配class包含article的元素 |
| ID选择器 | #header | 匹配id为header的元素 |
| 通配选择器 | * | 匹配所有元素 |
Class选择器在实际项目中使用非常频繁。例如:
<p class="notice">提示内容</p>
对应CSS:
.notice {
color: red;
}一个元素还可以同时拥有多个class:
<button class="button primary">提交</button>
如果只想选择同时拥有两个class的元素,可以写:
.button.primary {
font-weight: bold;
}这里两个选择器之间没有空格,表示两个条件必须同时出现在同一个元素上。
空格和大于号有什么区别
CSS选择器中最容易混淆的地方之一,就是元素之间的空格和>。
假设HTML结构为:
<div class="article"> <p>第一层段落</p> <div> <p>第二层段落</p> </div> </div>
使用:
.article p {
color: blue;
}表示选择.article内部所有层级的p元素,因此两个段落都会匹配。
如果改成:
.article > p {
color: blue;
}>表示子元素组合器,只匹配直接子元素,因此只有第一层p会被选中。
这种区别在复杂导航、文章目录和组件样式中非常重要。如果本来只准备修改第一层元素,却使用了普通空格,深层子元素也可能意外继承对应规则。
相邻元素也可以直接选择
CSS还提供了处理同级元素关系的组合器。
例如:
h2 + p {
margin-top: 0;
}+表示相邻兄弟组合器,只选择紧跟在h2后面的第一个p元素。
如果使用:
h2 ~ p {
color: #666;
}~表示后续兄弟组合器,会匹配与h2拥有相同父元素,并且出现在h2后面的符合条件的p元素。
这类选择器适合处理文章标题后的首段、表单字段以及同级组件之间的关系,能够减少为了样式额外添加class的情况。
属性选择器适合定位什么
网页元素通常带有href、type、target、data-*等属性,CSS可以直接根据这些属性进行匹配。
例如选择所有带target属性的链接:
a[target] {
color: green;
}只选择在新窗口打开的链接:
a[target="_blank"] {
color: green;
}还可以根据属性值的部分内容进行匹配:
a[href^="https://"] {
/* href以https://开头 */
}
a[href$=".pdf"] {
/* href以.pdf结尾 */
}
a[href*="example.com"] {
/* href中包含example.com */
}属性选择器在网站分析和前端调试中也很实用。例如需要快速找到所有PDF链接、外部链接或者带某个data属性的组件,不一定非要修改HTML添加新的class。
伪类用于选择元素状态
伪类通常以一个冒号开头,可以根据元素状态、位置或者结构关系进行匹配。
最常见的是链接和交互状态:
a:hover {
color: red;
}
input:focus {
border-color: blue;
}
button:disabled {
opacity: 0.5;
}:hover表示鼠标悬停,:focus表示元素当前获得焦点,:disabled用于匹配被禁用的表单控件。
结构型伪类同样非常常见:
li:first-child {
font-weight: bold;
}
li:last-child {
margin-bottom: 0;
}
li:nth-child(2) {
color: red;
}:nth-child()还可以使用公式。例如选择奇数项:
tr:nth-child(odd) {
background: #f5f5f5;
}这类规则经常用于表格、列表和重复组件。
伪元素和伪类并不相同
伪元素通常使用双冒号,例如:
p::first-letter {
font-size: 2em;
}
.title::before {
content: "★";
}
.title::after {
content: "";
}伪类通常用于匹配一个已有元素的状态,而伪元素更接近选择元素中的某个抽象部分,或者创建可用于样式表现的生成内容。
::before和::after经常用于图标、装饰线条、角标以及其他不需要额外HTML标签的视觉效果。
:not()适合排除特定元素
如果希望选择一批元素,但排除其中某一种情况,可以使用:not()。
例如:
a:not(.button) {
text-decoration: underline;
}它表示选择所有a元素,但排除class为button的链接。
又比如:
input:not([type="submit"]) {
border: 1px solid #ccc;
}这样可以给大部分input设置样式,同时排除提交按钮。
相比不断增加覆盖规则,合理使用:not()可以让选择逻辑更加直接。
:is()可以减少重复写法
如果多个选择器包含大量相同结构,可以使用:is()减少重复。
例如:
.article h1,
.article h2,
.article h3 {
line-height: 1.4;
}可以写成:
.article :is(h1, h2, h3) {
line-height: 1.4;
}在复杂组件中,这种写法可以明显缩短选择器。不过需要注意,:is()在计算优先级时会受到参数列表中选择器权重的影响,所以不能只把它理解成简单的代码缩写。
:where()为什么值得了解
:where()在语法上和:is()很像:
:where(.article, .page) h2 {
margin-top: 2em;
}它最大的区别在于Specificity,也就是选择器优先级。
按照当前CSS规则,:where()自身的优先级始终是0。这使它非常适合编写容易被后续样式覆盖的基础规则。
例如组件库、主题或者网站公共样式希望限制适用范围,但又不希望产生过高的CSS权重时,:where()会比较实用。
:has()可以根据子元素反选父级
:has()是现代CSS选择器中很有代表性的一个功能。
过去CSS通常只能根据父元素向下寻找子元素,而:has()可以根据内部或相邻元素的情况选择当前元素。
例如:
.card:has(img) {
padding-top: 0;
}它表示只选择内部存在img元素的.card。
还可以检测表单状态:
.form-item:has(input:invalid) {
border-color: red;
}或者判断标题后面是否存在特定元素:
h2:has(+ p) {
margin-bottom: 10px;
}这类能力过去经常需要JavaScript或者额外class,现在一部分场景可以直接通过CSS实现。
多个条件可以组合使用
CSS选择器真正强大的地方,不在于单独记住某一种语法,而在于能够组合条件。
例如:
.article > p:first-child {
font-size: 18px;
}表示选择article中的直接子p,并且这个p必须是父元素中的第一个子元素。
再例如:
form input[type="email"]:focus {
border-color: blue;
}这里同时使用了元素选择器、后代组合器、属性选择器和伪类。
只要理解每一部分表达的条件,就不需要把复杂选择器当成一种新的语法死记。
CSS优先级是怎么计算的
当多个规则同时匹配一个元素,并且修改相同属性时,就需要涉及Specificity。
根据MDN当前说明,可以把常用选择器权重大致理解为三个级别:
| 类型 | 示例 | 权重类别 |
|---|---|---|
| ID | #header | 最高一组 |
| Class、属性、伪类 | .item、[type]、:hover | 中间一组 |
| 元素、伪元素 | div、::before | 基础一组 |
例如:
#main .title {
color: red;
}
.page .title {
color: blue;
}如果两个规则同时匹配,包含ID选择器的第一条通常拥有更高Specificity。
如果两条规则优先级相同,并且其他层叠条件也相同,则后出现的规则通常生效。
需要注意,CSS最终采用哪条声明并不是只看选择器数字,还涉及来源、!important、Cascade Layer、作用域和声明顺序等层叠机制。
为什么不建议大量使用!important
遇到样式无法覆盖时,很多人第一反应是:
color: red !important;
这虽然能够快速解决一部分冲突,却容易让后续维护越来越困难。
MDN目前也建议不要把!important作为解决Specificity问题的常规方式。更合理的做法是检查现有选择器为什么权重过高,并合理设计class和层叠关系。
如果一个项目到处需要继续追加更长的选择器和!important才能覆盖旧规则,通常说明CSS结构本身已经需要整理。
选择器也能用于JavaScript
CSS Selector并不限于CSS文件。
JavaScript中可以使用:
document.querySelector('.article')获取第一个匹配的元素。
使用:
document.querySelectorAll('.article a')则可以获取所有符合选择器条件的元素,并返回NodeList。
很多网页采集、自动化测试、浏览器Console调试和DOM分析都会直接使用CSS选择器。
例如要找出所有设置了原生懒加载的图片:
document.querySelectorAll('img[loading="lazy"]')或者查找所有PDF链接:
document.querySelectorAll('a[href$=".pdf"]')所以熟悉CSS选择器以后,网页排查和数据提取也会方便很多。
如何在浏览器测试选择器
如果不确定一个选择器到底匹配哪些元素,可以直接使用Chrome DevTools。
打开开发者工具进入Elements面板,使用查找功能输入CSS选择器,就可以检查页面中匹配的元素。
也可以进入Console执行:
document.querySelectorAll('.article > p')浏览器会直接返回匹配结果。
这种方式特别适合学习复杂选择器。与其只看语法说明,不如找一个真实网页测试>、+、属性选择器和:has()分别会匹配哪些元素。
poxiaoxi博客
精彩评论