CSS选择器

一、基础选择器


1.1 通配选择器 *

匹配规则:匹配页面中所有元素。

<div>div 元素</div>
<p>p 元素</p>
<span>span 元素</span>
* {
  color: red;
}

结果:div、p、span 全部变红。


1.2 标签选择器 div

匹配规则:匹配指定标签名的所有元素。

<div>div 元素</div>
<p>p 元素</p>
<div>另一个 div 元素</div>
div {
  color: red;
}

结果:两个 div 变红,p 不变。


1.3 类选择器 .class

匹配规则:匹配具有指定 class 属性的所有元素,可复用。

<div class="box">div 盒子</div>
<p class="box">p 盒子</p>
<span>span 元素</span>
.box {
  color: red;
}

结果:div 和 p 都变红,span 不变。


1.4 ID 选择器 #id

匹配规则:匹配具有指定 id 属性的元素,页面内唯一。

<div id="header">页头</div>
<div id="footer">页脚</div>
#header {
  color: red;
}

结果:只有”页头”变红,“页脚”不变。


1.5 存在性属性选择器 [attr]

匹配规则:匹配带有指定属性名的元素,不管属性值是什么。

<input type="text" placeholder="用户名">
<input placeholder="密码">
<button>登录</button>
[placeholder] {
  background: yellow;
}

结果:用户名框和密码框 背景变黄,button 不变(没有 placeholder 属性)。


1.6 完全匹配 [attr=“val”]

匹配规则:属性值完全等于指定值。

<input type="text">
<input type="password">
<input type="text">
[type="text"] {
  border: 2px solid red;
}

结果:两个 type=“text” 的输入框 加红边框,密码框不变。


1.7 开头匹配 [attr^=“val”]

匹配规则:属性值以指定值开头。

<a href="https://example.com">安全链接</a>
<a href="http://example.com">不安全链接</a>
[href^="https"] {
  color: green;
}

结果:第一个链接 变绿(以 https 开头),第二个不变(http 开头)。


1.8 结尾匹配 [attr$=“val”]

匹配规则:属性值以指定值结尾。

<img src="photo.jpg">
<img src="icon.png">
<img src="photo.gif">
[src$=".png"] {
  border: 3px solid blue;
}

结果:只有 icon.png 加蓝边框,其他不变。


1.9 包含匹配 [attr*=“val”]

匹配规则:属性值包含指定子串。

<div class="btn-primary">主要按钮</div>
<div class="btn-danger">危险按钮</div>
<div class="header">页头</div>
[class*="btn"] {
  color: white;
  background: black;
}

结果:前两个 div 变白字黑底(class 里包含”btn”),第三个不变。

二、组合选择器(层级与兄弟关系)

2.1 后代选择器(空格)A B

匹配规则:选中 A 内部所有层级的 B 元素,儿孙全算,不限层级。

<div class="box">
  <p>儿子 p</p>
  <div>
    <p>孙子 p</p>
  </div>
</div>
.box p {
  color: red;
}

结果:儿子 p 和孙子 p 全都变红。


2.2 子代选择器(大于号)A > B

匹配规则:只选 A 的直接子元素 B,隔一层就不算。

<div class="box">
  <p>儿子 p</p>
  <div>
    <p>孙子 p</p>
  </div>
</div>
.box > p {
  color: red;
}

结果:只有”儿子 p”变红。“孙子 p”被 <div> 包着,隔了一层,选不中。


2.3 相邻兄弟选择器(加号)A + B

匹配规则:选中紧跟在 A 后面的第一个同级兄弟 B,必须紧挨着,中间不能有任何标签。

<h2>标题</h2>
<p>紧挨着的 p</p>
<p>隔了一个的 p</p>
h2 + p {
  color: red;
}

结果:第一个 <p> 变红,第二个不变(不紧挨 h2)。


2.4 通用兄弟选择器(波浪号)A ~ B

匹配规则:选中 A 后面所有的同级兄弟 B,不要求紧挨,隔了其他标签也能继续抓。

<h2>标题</h2>
<p>第一个 p</p>
<div>中间隔了 div</div>
<p>第二个 p</p>
h2 ~ p {
  color: red;
}

结果:第一个 p 和第二个 p 全都变红,中间隔了 div 也不影响。


2.5 并集选择器(分组选择器)h1, .title, #main

匹配规则:用逗号 , 把多个选择器打成一组,同时选中其中任意一个命中的元素(相当于”或”的关系),常用于把多个元素的相同样式合并成一条规则。

<h1>一级标题</h1>
<p class="title">类名为 title 的段落</p>
<div id="main">id 为 main 的区块</div>
<span>普通 span</span>
h1, .title, #main {
  color: red;
}

结果:h1、.title、#main 三个元素全部变红,span 不变。

注意:逗号列表属于”非宽容”选择器——只要列表里任何一个选择器写错或浏览器不认识,整条规则都会失效。如果想让某一项失效不影响整体,可以用 :is() 包裹,但 :is() 的权重取括号内选择器的最高权重,与逗号列表里各项独立计算权重的方式不同。

三、伪类选择器(单冒号 :)

3.1 动态伪类

:hover

匹配规则:鼠标悬停时触发。

<button>悬停我</button>
button:hover {
  background: red;
  color: white;
}

结果:鼠标放上去,按钮 背景变红,字变白。


:focus

匹配规则:表单元素获得焦点时触发(点击输入框或 Tab 键切换)。

<input type="text" placeholder="点击我">
<input type="text" placeholder="点我试试">
input:focus {
  border: 3px solid blue;
  outline: none;
}

结果:点击哪个输入框,哪个就 加蓝边框。


匹配规则:匹配尚未被访问过的链接(带 href 属性的 <a>)。页面刚打开时,所有链接默认都处于 :link 状态。

<a href="https://example.com">未被访问的链接</a>
<a href="https://example.org">另一个链接</a>
a:link {
  color: blue;
  text-decoration: none;
}

结果:两个链接都显示为蓝色、无下划线(未访问状态)。


:visited

匹配规则:匹配已经被用户访问过(点击打开过)的链接。出于隐私保护,浏览器只允许 :visited 修改颜色类属性,其他样式一律无效。

<a href="https://example.com">访问过的链接</a>
a:visited {
  color: purple;
}

结果:访问过的链接变紫,未访问的链接保持 :link 的蓝色。


:active

匹配规则:匹配鼠标按下但还没松开的瞬间被激活的元素,常用于点击按钮时给出”按下”的反馈。

<button>点我</button>
button:active {
  transform: scale(0.95);
  background: #e0e0e0;
}

结果:按钮按下的瞬间缩小并变灰,松手后恢复。

注意:链接相关的伪类书写顺序必须为 :link → :visited → :hover → :active(记忆口诀:Love Hate),顺序错误会导致样式失效。


3.2 结构伪类

:first-child / :last-child

匹配规则:first-child 选父元素中的第一个子元素,last-child 选最后一个。

<ul>
  <li>第 1 项</li>
  <li>第 2 项</li>
  <li>第 3 项</li>
</ul>
li:first-child {
  color: red;
}
li:last-child {
  color: blue;
}

结果:第 1 项变红,第 3 项变蓝,第 2 项不变。


:nth-child(n)

匹配规则:选父元素中的第 n 个子元素。n 从 1 开始,也支持 odd(奇数)、even(偶数)、2n+1 等公式。

<ul>
  <li>第 1 项</li>
  <li>第 2 项</li>
  <li>第 3 项</li>
  <li>第 4 项</li>
</ul>
li:nth-child(2) {
  background: yellow;
}
li:nth-child(odd) {
  color: red;
}
li:nth-child(even) {
  color: blue;
}

结果:

· 第 2 项 黄色背景 · 第 1、3 项 红色字(奇数) · 第 2、4 项 蓝色字(偶数)


:nth-of-type(n)

匹配规则:在同类型标签中选第 n 个,忽略其他类型的标签干扰。

<div>
  <p>第 1 个 p</p>
  <span>span</span>
  <p>第 2 个 p</p>
  <p>第 3 个 p</p>
</div>
p:nth-of-type(2) {
  color: red;
}

结果:只有”第 2 个 p”变红。如果是用 :nth-child(2),选中的反而是 span。


:nth-last-child(n)

匹配规则:从父元素的最后一个子元素往前数,选第 n 个,同样支持 odd、even、2n+1 等公式。

<ul>
  <li>第 1 项</li>
  <li>第 2 项</li>
  <li>第 3 项</li>
  <li>第 4 项</li>
</ul>
li:nth-last-child(1) {
  color: red;
}

结果:倒数第 1 个(第 4 项)变红。


:first-of-type

匹配规则:选父元素中同类型的第一个子元素。与 :first-child 的区别::first-child 要求必须是”第一个孩子且类型匹配”,:first-of-type 只要求是”同类型中第一个出现的”,前面有其他标签挡着也没关系。

<div>
  <span>span</span>
  <p>第 1 个 p</p>
  <p>第 2 个 p</p>
</div>
p:first-of-type {
  color: red;
}

结果:“第 1 个 p”变红(它是 p 元素中第一个出现的);如果改用 p:first-child 则选不中(它前面还有 span)。


:last-of-type

匹配规则:选父元素中同类型的最后一个子元素,规则和 :first-of-type 正好相反。

<div>
  <p>第 1 个 p</p>
  <span>span</span>
  <p>第 2 个 p</p>
</div>
p:last-of-type {
  color: red;
}

结果:“第 2 个 p”变红,span 不影响计数。


:only-child

匹配规则:选父元素中唯一的那个子元素。

<div>
  <p>唯一的 p</p>
</div>
<div>
  <p>第 1 个 p</p>
  <p>第 2 个 p</p>
</div>
p:only-child {
  color: red;
}

结果:第一个 div 里的 p 变红(它是唯一子元素);第二个 div 有两个 p,都不算唯一子元素,不变。


:only-of-type

匹配规则:选父元素中同类型里唯一的元素,允许旁边有其他不同类型的兄弟元素。

<div>
  <p>唯一的 p</p>
  <span>span</span>
</div>
<div>
  <p>第 1 个 p</p>
  <p>第 2 个 p</p>
</div>
p:only-of-type {
  color: red;
}

结果:第一个 div 里的 p 变红(虽然旁边有 span,但 p 类型只有它一个);第二个 div 有两个 p,不变。


:root

匹配规则:匹配文档根元素,即 <html>。最常用于在根元素上声明全局 CSS 自定义变量(变量对整棵文档树的所有后代生效)。

<div>普通文字</div>
/* 在根元素声明全局变量 */
:root {
  --main-color: #e74c3c;
}

div {
  color: var(--main-color); /* 任何后代元素都可以使用 */
}

结果:div 的文字颜色使用 :root 声明的变量 —main-color(红色)。


:empty

匹配规则:匹配没有任何子节点的元素——不能有标签、不能有文本,连空格都不能有,常用于空状态占位。

<div class="empty"></div>
<div>有文本</div>
<div class="empty"> </div>
.empty:empty {
  background: #eee;
  height: 50px;
}

结果:第一个空 div 显示灰底占位;第二个有文本、第三个里面有空格都不算空,样式不生效。


:target

匹配规则:匹配当前 URL 锚点(#id)指向的元素,可用于纯 CSS 实现手风琴、选项卡切换等交互。

<a href="#section-1">打开区块一</a>
<div id="section-1">区块一的内容</div>
<div id="section-2">区块二的内容</div>
:target {
  background: yellow;
}

结果:当 URL 变成 页面地址#section-1 时,对应的区块一出现黄底;点击其他锚点链接则跟着切换。


说明:div:first-child(必须是第一个孩子且为 div)与 div:first-of-type(第一个出现的 div)含义不同,使用时需注意区分。


3.3 表单状态伪类

表单相关的状态伪类数量多、逻辑独立,在日常开发中使用频率很高,因此单独拆成独立章节。

:enabled / :disabled

匹配规则::enabled 匹配可编辑、可用的表单控件;:disabled 匹配被禁用的表单控件(设置了 disabled 属性)。

<input type="text" placeholder="可用">
<input type="text" placeholder="禁用" disabled>
input:enabled {
  background: white;
}

input:disabled {
  background: #eee;
  color: #999;
}

结果:第一个输入框保持白底;第二个变灰底灰字(禁用状态,无法编辑)。


:read-only / :read-write

匹配规则::read-only 匹配只读的表单控件(设置了 readonly 属性);:read-write 匹配可读写的表单控件(默认状态)。

<input type="text" value="只读框" readonly>
<input type="text" placeholder="可编辑框">
input:read-only {
  background: #f5f5f5;
  border: 1px dashed #999;
}

input:read-write {
  background: white;
}

结果:只读框灰底虚线边框,可编辑框保持白底。


:required / :optional

匹配规则::required 匹配设置了 required 属性的必填控件;:optional 匹配没设 required 的非必填控件。

<form>
  <input type="text" required placeholder="必填项">
  <input type="text" placeholder="选填项">
</form>
input:required {
  border: 2px solid #e74c3c;
}

input:optional {
  border: 2px solid #ccc;
}

结果:必填框红边框,选填框灰边框。


:valid / :invalid

匹配规则::valid 匹配内容通过浏览器内置校验规则的表单控件;:invalid 匹配校验失败的控件(比如 type=“email” 的内容不是邮箱格式)。

<form>
  <input type="email" placeholder="邮箱" value="hello">
  <input type="email" placeholder="邮箱" value="[email protected]">
</form>
input:valid {
  border: 2px solid #2ecc71;
}

input:invalid {
  border: 2px solid #e74c3c;
}

结果:第一个邮箱格式错误变红框,第二个格式正确变绿框。


:in-range / :out-of-range

匹配规则::in-range 匹配数值在 min / max 范围内的输入框;:out-of-range 匹配超出范围的值。

<input type="number" min="0" max="10" value="5">
<input type="number" min="0" max="10" value="20">
input:in-range {
  border: 2px solid #2ecc71;
}

input:out-of-range {
  border: 2px solid #e74c3c;
}

结果:5 在 0~10 范围内变绿框;20 超出范围变红框。


:placeholder-shown

匹配规则:匹配当前正在显示占位文本(placeholder)的输入框,常用于判断用户是否还没输入内容。

<input type="text" placeholder="请输入用户名">
input:placeholder-shown {
  border: 2px dashed #999;
}

结果:输入框显示占位文字时是灰色虚线边框;一旦用户输入内容,占位文字消失,样式自动取消。


:checked

匹配规则:单选框或复选框被选中时触发。

<input type="checkbox"> 接受条款
<input type="checkbox" checked> 已勾选
input:checked {
  outline: 2px solid red;
}

结果:默认勾选的那个 出现红描边,另一个不变。


:default

匹配规则:匹配表单中默认被选中的元素(例如带 checked 属性的单选框/复选框,或带 selected 属性的 option),页面刷新或重置后仍指向它。

<form>
  <input type="radio" name="opt" checked> 选项一
  <input type="radio" name="opt"> 选项二
</form>
input:default {
  outline: 2px solid blue;
}

结果:选项一(默认选中项)加蓝描边;用户手动切换选中其他选项后,:default 仍然指向最初的默认项。


:indeterminate

匹配规则:匹配处于”不确定”状态的复选框或单选框,典型场景是”全选框”的部分选中(半选)状态。

<input type="checkbox" id="all"> 全选
<input type="checkbox" checked> 子项一
<input type="checkbox"> 子项二
#all:indeterminate {
  outline: 2px solid orange;
}

结果:当通过 JS 将全选框的 indeterminate 属性设为 true 后,它呈现半选状态(框内显示一条横线)并加橙描边;该状态需要配合 JS 触发。


3.4 逻辑与关系伪类

这类选择器不按标签、类、ID 或属性来选元素,而是按逻辑关系或特殊匹配方式来选,功能更灵活。

否定选择器 :not()

匹配规则:选中不匹配括号里选择器的元素(排除法)。

<ul>
  <li>第 1 项</li>
  <li class="active">第 2 项(激活)</li>
  <li>第 3 项</li>
</ul>
<input type="text" disabled>
<input type="text" placeholder="可用">
/* 排除 class="active" 的 li */
li:not(.active) {
  color: gray;
}

/* 排除禁用状态,且获得焦点的输入框(多选择器连用) */
input:not(:disabled):focus {
  border-color: blue;
}

结果:第 1、3 项变灰(第 2 项不变);可用的输入框获得焦点时加蓝边框(禁用的输入框无法获得焦点,所以不变)。


任意匹配选择器 :is()

匹配规则:将多个选择器打包分组,匹配其中任意一个就能命中(相当于”或”的关系)。

<header class="header"><p>头部段落</p></header>
<footer class="footer"><p>底部段落</p></footer>
<section><p>普通段落</p></section>
/* 等价于 .header p, .footer p,且权重等于 0,0,1,1(类+标签) */
:is(.header, .footer) p {
  color: red;
}

结果:头部和底部的段落变红,普通段落不变。

:is() 在旧浏览器兼容性不如 :where(),使用时注意。


零权重分组选择器 :where()

匹配规则:和 :is() 用法完全一样,也是打包分组。唯一区别:权重恒为 0,哪怕括号里写 #id,整体权重也是 0,0,0,0。

<nav class="nav"><a href="#">首页</a></nav>
<footer class="footer"><a href="#">隐私</a></footer>
<div class="special"><a href="#">特别链接</a></div>
/* 等价于 .nav a, .footer a,但权重仅为 a(0,0,0,1) */
:where(.nav, .footer) a {
  color: blue;
}

/* 一个类选择器(权重 0,0,1,1)就能轻松覆盖上面的蓝色 */
.special a {
  color: red;
}

结果:导航和页脚的链接默认蓝色;而 .special 里的链接变红(无需 !important 就能覆盖)。


关系选择器 :has()

匹配规则:选中包含某个子元素或满足某种条件的父元素(相当于”父级选择器”)。

<div class="card">
  <h2>标题</h2>
  <p>内容段落</p>
</div>
<div class="card">
  <h2>标题</h2>
</div>
/* 选中包含 <p> 子元素的 .card */
.card:has(p) {
  border: 2px solid red;
}

结果:第一个 .card 加红边框(因为里面有 <p>),第二个不变。

兼容性注意::has() 是较新的选择器,部分旧浏览器不支持,现代项目可用。


3.5 伪类组合连写(通用规则)

多个选择器(含伪类)可以紧挨着连写(中间无空格),表示”且”的关系(必须同时满足所有条件)。

/* 悬停且被激活的链接 */
a:hover:active {
  color: orange;
}

/* 第一个子元素且被悬停的 li */
li:first-child:hover {
  background: gray;
}

/* 结合 :not() 使用:未被禁用且获得焦点的输入框 */
input:not(:disabled):focus {
  border-color: blue;
}

两个致命细节:

  1. 顺序影响效果(LVHA 规则): 针对 <a> 标签,伪类必须按 :link → :visited → :hover → :active 顺序写(记忆口诀:Love Hate),否则在部分浏览器中可能失效。
  2. 伪元素必须放最后: 如果混用伪类与伪元素(如 ::before),伪元素必须写在所有伪类后面。 正确:div:hover::before { } 错误:div::before:hover { }(浏览器无法解析,直接忽略)

权重累加:链式组合的伪类越多,权重越高(如 div:hover:not(.active) 权重为 0,0,2,1)。

四、伪元素(双冒号 ::)

4.1 ::before / ::after

匹配规则:在元素内部最前面(before)或最后面(after)创建一个虚拟子元素,必须写 content 属性才能显示。

<div class="box">内容</div>
.box::before {
  content: "【前缀】";
  color: red;
}
.box::after {
  content: "【后缀】";
  color: blue;
}

结果:页面上显示为 【前缀】内容【后缀】 ,前缀红字,后缀蓝字。


4.2 ::first-line

匹配规则:选中块级元素中的第一行文本(根据浏览器中显示的换行的情况,不是根据代码里面的回车之类的换行)。

<p>这是第一行,这是第一行,这是第一行,这是第一行,这是第一行,这是第一行,这是第一行。</p>
p::first-line {
  font-weight: bold;
  color: red;
}

结果:第一行变粗变红,后面行不变。


4.3 ::first-letter

匹配规则:选中块级元素的首字母(中文即第一个字),常用于制作首字下沉效果。

<p>山重水复疑无路,柳暗花明又一村。</p>
p::first-letter {
  font-size: 2em;
  font-weight: bold;
  color: red;
}

结果:首字”山”放大加粗变红,形成首字下沉的视觉效果。


4.4 ::selection

匹配规则:匹配用户用鼠标高亮选中的文本区域,可自定义选中文字的颜色和背景。

<p>选中我试试看。</p>
::selection {
  background: #ffd700;
  color: #000;
}

结果:页面任意文字被选中时,背景金黄、文字变黑(覆盖浏览器默认的蓝底白字)。


4.5 ::placeholder

匹配规则:匹配输入框的占位提示文本,用来单独给占位文字设置样式,只能修改颜色等少数文字属性。

<input type="text" placeholder="请输入用户名">
input::placeholder {
  color: #999;
  font-style: italic;
}

结果:占位文字显示为灰字斜体;用户输入内容后占位文本消失,样式不再生效。


4.6 ::marker

匹配规则:匹配列表项前面的项目标记符号(圆点、序号等),可修改标记的颜色、字号等。

<ul>
  <li>苹果</li>
  <li>香蕉</li>
</ul>
li::marker {
  color: red;
  font-weight: bold;
}

结果:列表前的圆点标记变红加粗,列表文字本身不变。


4.7 ::backdrop

匹配规则:匹配全屏模式(Fullscreen API)或 <dialog> 弹窗背后的背景层,常用来给弹窗加一层半透明蒙层。

<dialog open>
  <p>弹窗内容</p>
</dialog>
dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

结果:弹窗后面的区域出现半透明黑色蒙层,突出弹窗内容。

五、优先级权重(防冲突)

当多个选择器同时指向同一个元素时,哪个规则生效?按权重从高到低排列:

类型权重值示例
行内样式1,0,0,0<div style="color:red;">
ID 选择器0,1,0,0#header
类 / 伪类 / 属性选择器0,0,1,0.box、:hover、[type="text"]
标签 / 伪元素0,0,0,1div、::before
通配符 / 组合符0,0,0,0*、>、+、~
<div id="box" class="box" style="color: yellow;">什么颜色?</div>
#box { color: red; }      /* ID 权重 0100 */
.box { color: blue; }     /* 类 权重 0010 */
div { color: green; }     /* 标签 权重 0001 */

结果:文字是黄色。行内样式权重最高(1000),压过了所有外部样式。

选择器权重计算规则如下:

  • :not() 本身的权重不计,总权重由括号内选择器的最高权重决定。例如 :not(.box) 权重等于 .box(0,0,1,0),而 :not(#id) 权重等于 #id(0,1,0,0)。
  • :is() 取括号内选择器的最高权重(如 :is(.header, .footer) 权重等于类选择器 0,0,1,0)。
  • :where() 权重恒为 0,哪怕括号里写 #id,整体权重也是 0,0,0,0。专门用于”低优先级兜底样式”。
  • :has() 的权重计算方式与 :is() 相同,取括号内选择器的最高权重;但它在匹配时需要遍历相关节点,性能开销比普通选择器大,建议谨慎使用。
  • !important 拥有最高特权(具体用法看下面),日常开发尽量避免:它会破坏 CSS 层叠规则,导致后期修改只能不断追加 !important,维护成本极高。仅限极少数场景(如覆盖第三方库且无其他办法)才谨慎使用。

注意:!important 不属于选择器,它是写在属性值后面的修饰符。

div {
  color: green !important; /* 无论外部样式权重多少,这条强制生效,除非另一个与其冲突的也使用了!important且权重更高 */
}

CSS选择器

https://ym.2v.nz/posts/devnotes/css/selector/
作者 羽毛
发布于 2026-09-08
许可协议 CC BY-NC-SA 4.0