1. 什么是用户行为伪类
在 CSS 中,伪类(Pseudo-class)用于描述元素的特定状态 。其中一类伪类专门用来表达「用户与元素发生了交互」,我们称之为用户行为伪类(User Action Pseudo-classes)。
最常见的两个就是:
:hover:鼠标指针悬停在元素上时触发:focus:元素获得焦点时触发(如点击输入框、Tab 键切换)
它们让页面从「静态展示」变成「可感知的交互界面」,是提升可用性与视觉反馈的基础手段。
2. :hover ------ 鼠标悬停
2.1 基本用法
:hover 在鼠标指针进入元素区域时生效,移出后失效。
css
.button {
background-color: #1e88e5;
color: #fff;
transition: background-color 0.2s ease;
}
.button:hover {
background-color: #1565c0;
}
上面的例子中,鼠标移入按钮时背景色变深,配合 transition 得到平滑过渡。
2.2 常见应用场景
- 导航菜单项高亮
- 表格行悬停高亮,方便横向阅读
- 卡片阴影加深,营造「浮起」效果
- 图片轻微放大或显示遮罩层
css
.table-row:hover {
background-color: #f5f7fa;
}
.card {
transition: box-shadow 0.25s ease, transform 0.25s ease;
}
.card:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
}
2.3 注意事项
- 移动端没有真正的 hover 。触摸设备上
:hover的行为不可靠,可能表现为「点击后一直保持悬停态」。因此不要把关键信息或功能只放在:hover里。 - 建议用
@media (hover: hover)包裹悬停样式,只在支持悬停的设备上生效:
css
@media (hover: hover) and (pointer: fine) {
.card:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
}
:hover会沿 DOM 树向上冒泡影响祖先元素,注意选择器范围,避免误伤。
3. :focus ------ 获得焦点
3.1 基本用法
:focus 在元素成为当前焦点时生效,典型场景是表单控件被点击或通过 Tab 键选中。
css
.input {
border: 1px solid #ccc;
outline: none;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.input:focus {
border-color: #1e88e5;
box-shadow: 0 0 0 3px rgba(30, 136, 229, 0.2);
}
3.2 可聚焦元素
并非所有元素都能获得焦点。默认可聚焦的元素包括:
- 表单控件:
input、select、textarea、button - 链接:
a[href] - 带
tabindex属性的元素(tabindex="0"或负值)
如果想让普通 div 也能聚焦,需要显式添加 tabindex:
html
<div class="card" tabindex="0">可聚焦的卡片</div>
3.3 不要随意移除焦点轮廓
很多开发者习惯写 outline: none 去掉默认焦点框,但这会严重损害键盘用户的可访问性------他们无法判断当前焦点在哪。
正确做法是替换 而不是删除:
css
/* 不推荐 */
.input:focus {
outline: none;
}
/* 推荐:用更清晰的样式替代 */
.input:focus-visible {
outline: 2px solid #1e88e5;
outline-offset: 2px;
}
4. :focus-visible 与 :focus-within
4.1 :focus-visible
:focus-visible 只在浏览器判断「用户需要看到焦点指示」时生效。典型表现是:
- 鼠标点击按钮 → 通常不 触发
:focus-visible - 键盘 Tab 切换 → 触发
:focus-visible
这样既能给键盘用户清晰的焦点提示,又不会在鼠标点击时出现多余的轮廓。
css
.button:focus-visible {
outline: 2px solid #1565c0;
outline-offset: 2px;
}
4.2 :focus-within
:focus-within 在元素自身或其后代获得焦点时生效,非常适合做表单容器的整体高亮。
css
.form-group:focus-within {
border-color: #1e88e5;
background-color: #f5f9ff;
}
html
<div class="form-group">
<label for="email">邮箱</label>
<input id="email" type="email" />
</div>
当输入框获得焦点时,整个 .form-group 都会高亮,视觉上更完整。
5. 组合使用与优先级
多个伪类可以叠加,表示「同时满足」:
css
/* 鼠标悬停且获得焦点 */
.button:hover:focus {
background-color: #0d47a1;
}
/* 悬停时,若内部有元素获得焦点,则高亮 */
.card:hover:focus-within {
border-color: #1e88e5;
}
需要注意 CSS 优先级(Specificity):伪类与类选择器权重相同,均为 0,1,0。因此 :hover 与 :focus 叠加时权重会累加,写样式时留意覆盖顺序。
6. 状态流转示意
#mermaid-svg-sp432c9iplFmD5QH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sp432c9iplFmD5QH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sp432c9iplFmD5QH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sp432c9iplFmD5QH .error-icon{fill:#552222;}#mermaid-svg-sp432c9iplFmD5QH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sp432c9iplFmD5QH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sp432c9iplFmD5QH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sp432c9iplFmD5QH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sp432c9iplFmD5QH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sp432c9iplFmD5QH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sp432c9iplFmD5QH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sp432c9iplFmD5QH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sp432c9iplFmD5QH .marker.cross{stroke:#333333;}#mermaid-svg-sp432c9iplFmD5QH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sp432c9iplFmD5QH p{margin:0;}#mermaid-svg-sp432c9iplFmD5QH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sp432c9iplFmD5QH .cluster-label text{fill:#333;}#mermaid-svg-sp432c9iplFmD5QH .cluster-label span{color:#333;}#mermaid-svg-sp432c9iplFmD5QH .cluster-label span p{background-color:transparent;}#mermaid-svg-sp432c9iplFmD5QH .label text,#mermaid-svg-sp432c9iplFmD5QH span{fill:#333;color:#333;}#mermaid-svg-sp432c9iplFmD5QH .node rect,#mermaid-svg-sp432c9iplFmD5QH .node circle,#mermaid-svg-sp432c9iplFmD5QH .node ellipse,#mermaid-svg-sp432c9iplFmD5QH .node polygon,#mermaid-svg-sp432c9iplFmD5QH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sp432c9iplFmD5QH .rough-node .label text,#mermaid-svg-sp432c9iplFmD5QH .node .label text,#mermaid-svg-sp432c9iplFmD5QH .image-shape .label,#mermaid-svg-sp432c9iplFmD5QH .icon-shape .label{text-anchor:middle;}#mermaid-svg-sp432c9iplFmD5QH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sp432c9iplFmD5QH .rough-node .label,#mermaid-svg-sp432c9iplFmD5QH .node .label,#mermaid-svg-sp432c9iplFmD5QH .image-shape .label,#mermaid-svg-sp432c9iplFmD5QH .icon-shape .label{text-align:center;}#mermaid-svg-sp432c9iplFmD5QH .node.clickable{cursor:pointer;}#mermaid-svg-sp432c9iplFmD5QH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sp432c9iplFmD5QH .arrowheadPath{fill:#333333;}#mermaid-svg-sp432c9iplFmD5QH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sp432c9iplFmD5QH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sp432c9iplFmD5QH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sp432c9iplFmD5QH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sp432c9iplFmD5QH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sp432c9iplFmD5QH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sp432c9iplFmD5QH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sp432c9iplFmD5QH .cluster text{fill:#333;}#mermaid-svg-sp432c9iplFmD5QH .cluster span{color:#333;}#mermaid-svg-sp432c9iplFmD5QH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sp432c9iplFmD5QH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sp432c9iplFmD5QH rect.text{fill:none;stroke-width:0;}#mermaid-svg-sp432c9iplFmD5QH .icon-shape,#mermaid-svg-sp432c9iplFmD5QH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sp432c9iplFmD5QH .icon-shape p,#mermaid-svg-sp432c9iplFmD5QH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sp432c9iplFmD5QH .icon-shape .label rect,#mermaid-svg-sp432c9iplFmD5QH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sp432c9iplFmD5QH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sp432c9iplFmD5QH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sp432c9iplFmD5QH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 鼠标移入
鼠标移出
点击 / Tab 聚焦
失焦 blur
移入并聚焦
移出或失焦
默认状态
hover 状态
focus 状态
hover + focus
7. 可访问性最佳实践
- 永远不要用
outline: none直接抹掉焦点样式 ,用:focus-visible提供更清晰的替代方案。 - 不要只依赖颜色变化表达状态,可配合边框、阴影、下划线等多种视觉线索。
- 保证对比度:悬停/聚焦态与默认态之间应有足够差异,同时文字与背景对比度满足 WCAG 要求。
- 移动端优先 :交互反馈不要只依赖
:hover,触摸设备上应通过:active或点击态提供反馈。 - 保持过渡自然 :给
transition设置 150--250ms 的时长,避免过慢导致操作迟滞。
8. 小结
| 伪类 | 触发条件 | 典型用途 |
|---|---|---|
:hover |
鼠标悬停 | 按钮高亮、卡片浮起、行高亮 |
:focus |
元素获得焦点 | 输入框边框、按钮焦点态 |
:focus-visible |
需要显示焦点指示时 | 键盘导航的焦点轮廓 |
:focus-within |
自身或后代获得焦点 | 表单容器整体高亮 |
:hover 与 :focus 是构建交互反馈的基石。用好它们的关键在于:既要有明确的视觉反馈,又不能牺牲可访问性 。配合 :focus-visible、:focus-within 以及媒体查询,可以写出既美观又对键盘与触摸用户友好的样式。