Skip to content

伪类与伪元素

伪类和伪元素让你可以选择特定状态的元素或元素的特定部分。它们是 CSS 选择器的重要扩展,能实现很多常规选择器无法做到的效果。

伪类 vs 伪元素#

🎯 区分方法:伪类用单冒号 :,伪元素用双冒号 ::(为了向后兼容,老的伪元素如 ::before 也接受单冒号)。

常用伪类#

交互状态伪类#

/* 鼠标悬停 */
a:hover {
color: #3b82f6;
}
/* 获得焦点 */
input:focus {
border-color: #3b82f6;
outline: none;
}
/* 激活(按下瞬间) */
button:active {
transform: scale(0.98);
}
/* 已访问的链接 */
a:visited {
color: #6b7280;
}

表单状态伪类#

/* 禁用状态 */
input:disabled {
background-color: #f3f4f6;
cursor: not-allowed;
}
/* 启用状态 */
input:enabled {
background-color: white;
}
/* 选中的复选框/单选 */
input:checked {
accent-color: #3b82f6;
}
/* 必填字段 */
input:required {
border-left: 3px solid #ef4444;
}
/* 可选字段 */
input:optional {
border-left: 3px solid #10b981;
}
/* 验证通过 */
input:valid {
border-color: #10b981;
}
/* 验证失败 */
input:invalid {
border-color: #ef4444;
}
/* 只读 */
input:read-only {
background-color: #f9fafb;
}
/* 显示占位符时 */
input:placeholder-shown {
font-style: italic;
}

结构伪类#

/* 第一个子元素 */
li:first-child {
font-weight: bold;
}
/* 最后一个子元素 */
li:last-child {
border-bottom: none;
}
/* 唯一子元素 */
p:only-child {
margin: 0;
}
/* 第 n 个子元素 */
li:nth-child(3) {
color: red;
}
/* 奇数位置 */
tr:nth-child(odd) {
background-color: #f9fafb;
}
/* 偶数位置 */
tr:nth-child(even) {
background-color: white;
}
/* 每第 3 个 */
li:nth-child(3n) {
color: blue;
}
/* 从第 3 个开始每隔 2 个 */
li:nth-child(2n + 3) {
font-style: italic;
}
/* 前 3 个 */
li:nth-child(-n + 3) {
font-weight: bold;
}
/* 倒数第 n 个 */
li:nth-last-child(2) {
color: gray;
}

类型伪类#

/* 同类型的第一个 */
p:first-of-type {
font-size: 1.2em;
}
/* 同类型的最后一个 */
p:last-of-type {
margin-bottom: 0;
}
/* 同类型的第 n 个 */
img:nth-of-type(2) {
float: right;
}
/* 唯一该类型的元素 */
img:only-of-type {
display: block;
margin: 0 auto;
}

其他伪类#

/* 空元素 */
div:empty {
display: none;
}
/* 根元素 */
:root {
--primary-color: #3b82f6;
}
/* 目标元素(URL 锚点匹配) */
section:target {
background-color: #fef3c7;
}
/* 焦点在子元素内 */
.form-group:focus-within {
border-color: #3b82f6;
}
/* 仅键盘焦点 */
button:focus-visible {
outline: 2px solid #3b82f6;
}

伪元素#

::before 和 ::after#

在元素内容前/后插入内容:

/* 必须有 content 属性 */
.quote::before {
content: '' ';
}
.quote::after {
content: ' '';
}
/* 装饰性图标 */
.external-link::after {
content: '';
font-size: 0.75em;
}
/* 清除浮动 */
.clearfix::after {
content: '';
display: block;
clear: both;
}

content 属性值#

/* 文本 */
.note::before {
content: '注意:';
}
/* 属性值 */
a::after {
content: ' (' attr(href) ')';
}
/* 计数器 */
li::before {
content: counter(item) '. ';
counter-increment: item;
}
/* 图片 */
.icon::before {
content: url('icon.svg');
}
/* 空字符串(用于纯装饰) */
.decorated::before {
content: '';
display: block;
width: 50px;
height: 2px;
background: #3b82f6;
}

::first-line 和 ::first-letter#

/* 首行 */
p::first-line {
font-weight: bold;
color: #1f2937;
}
/* 首字母(大写字母效果) */
.article p:first-of-type::first-letter {
font-size: 3em;
float: left;
line-height: 1;
margin-right: 0.1em;
color: #3b82f6;
}

::selection#

选中文本的样式:

::selection {
background-color: #3b82f6;
color: white;
}
/* 特定元素的选中样式 */
.code::selection {
background-color: #fef3c7;
color: #1f2937;
}

::placeholder#

输入框占位符样式:

input::placeholder {
color: #9ca3af;
font-style: italic;
}

::marker#

列表标记样式:

li::marker {
color: #3b82f6;
font-weight: bold;
}
ol li::marker {
content: counters(list-item, '.') ' ';
}

实战案例#

自定义复选框#

.checkbox {
position: relative;
display: inline-flex;
align-items: center;
cursor: pointer;
}
.checkbox input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.checkbox .checkmark {
width: 20px;
height: 20px;
border: 2px solid #d1d5db;
border-radius: 4px;
margin-right: 0.5rem;
transition: all 0.2s;
}
/* 选中状态 */
.checkbox input:checked + .checkmark {
background-color: #3b82f6;
border-color: #3b82f6;
}
/* 选中后的勾 */
.checkbox input:checked + .checkmark::after {
content: '';
position: absolute;
left: 7px;
top: 3px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
/* 焦点状态 */
.checkbox input:focus-visible + .checkmark {
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
}
/* 禁用状态 */
.checkbox input:disabled + .checkmark {
background-color: #f3f4f6;
cursor: not-allowed;
}
<label class="checkbox">
<input type="checkbox" />
<span class="checkmark"></span>
记住我
</label>

工具提示#

.tooltip {
position: relative;
cursor: help;
}
.tooltip::after {
content: attr(data-tip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
padding: 0.5rem 0.75rem;
margin-bottom: 8px;
background-color: #1f2937;
color: white;
font-size: 0.875rem;
white-space: nowrap;
border-radius: 6px;
opacity: 0;
visibility: hidden;
transition: all 0.2s;
z-index: 100;
}
/* 小三角 */
.tooltip::before {
content: '';
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #1f2937;
opacity: 0;
visibility: hidden;
transition: all 0.2s;
}
.tooltip:hover::after,
.tooltip:hover::before {
opacity: 1;
visibility: visible;
}
<span class="tooltip" data-tip="这是提示内容">悬停查看</span>

表单验证反馈#

.form-group {
position: relative;
margin-bottom: 1.5rem;
}
.form-input {
width: 100%;
padding: 0.75rem 1rem;
padding-right: 2.5rem;
border: 1px solid #d1d5db;
border-radius: 6px;
transition: border-color 0.2s;
}
.form-input:focus {
outline: none;
border-color: #3b82f6;
}
/* 验证图标位置 */
.form-group::after {
position: absolute;
right: 0.75rem;
top: 50%;
transform: translateY(-50%);
font-size: 1.25rem;
}
/* 验证通过 */
.form-input:valid:not(:placeholder-shown) {
border-color: #10b981;
}
.form-group:has(.form-input:valid:not(:placeholder-shown))::after {
content: '';
color: #10b981;
}
/* 验证失败 */
.form-input:invalid:not(:placeholder-shown) {
border-color: #ef4444;
}
.form-group:has(.form-input:invalid:not(:placeholder-shown))::after {
content: '';
color: #ef4444;
}

斑马纹表格#

.table {
width: 100%;
border-collapse: collapse;
}
.table th,
.table td {
padding: 0.75rem 1rem;
text-align: left;
border-bottom: 1px solid #e5e7eb;
}
.table th {
background-color: #f9fafb;
font-weight: 600;
}
/* 斑马纹 */
.table tbody tr:nth-child(odd) {
background-color: #f9fafb;
}
/* 悬停高亮 */
.table tbody tr:hover {
background-color: #f3f4f6;
}
/* 首列加粗 */
.table td:first-child {
font-weight: 500;
}
/* 最后一行无边框 */
.table tbody tr:last-child td {
border-bottom: none;
}

导航高亮#

.nav {
display: flex;
gap: 0.5rem;
}
.nav-link {
padding: 0.5rem 1rem;
color: #6b7280;
text-decoration: none;
border-radius: 6px;
transition: all 0.2s;
}
.nav-link:hover {
color: #1f2937;
background-color: #f3f4f6;
}
/* 当前页面 */
.nav-link[aria-current='page'] {
color: #3b82f6;
background-color: #eff6ff;
}
/* 或使用 :target */
.nav-link:target {
color: #3b82f6;
background-color: #eff6ff;
}

响应式隐藏/显示#

/* 使用伪类和媒体查询组合 */
.mobile-only {
display: block;
}
.desktop-only {
display: none;
}
@media (min-width: 768px) {
.mobile-only {
display: none;
}
.desktop-only {
display: block;
}
}
/* 空状态提示 */
.list:empty::before {
content: '暂无数据';
display: block;
padding: 2rem;
text-align: center;
color: #9ca3af;
}

常见问题#

🤔 ::before 和 ::after 不显示?

  1. 必须有 content 属性,哪怕是空字符串
  2. 检查父元素是否是替换元素(如 imginputbr),它们不支持伪元素

🤔 不生效?

确保元素真的是父元素的第一个子元素。如果前面有空白文本节点或注释,可能会影响结果。考虑使用 :first-of-type

🤔 在触屏设备上不工作?

触屏设备没有真正的”悬停”状态,可以用 :active 或 JavaScript 处理触摸事件。


伪类和伪元素是 CSS 选择器的强大扩展。合理使用它们可以减少 HTML 标记、增强交互体验、实现优雅的装饰效果。下一篇我们将学习现代选择器,如 :is():where():has()