CSS 用户行为伪类::hover 与 :focus 详解

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. 可访问性最佳实践

  1. 永远不要用 outline: none 直接抹掉焦点样式 ,用 :focus-visible 提供更清晰的替代方案。
  2. 不要只依赖颜色变化表达状态,可配合边框、阴影、下划线等多种视觉线索。
  3. 保证对比度:悬停/聚焦态与默认态之间应有足够差异,同时文字与背景对比度满足 WCAG 要求。
  4. 移动端优先 :交互反馈不要只依赖 :hover,触摸设备上应通过 :active 或点击态提供反馈。
  5. 保持过渡自然 :给 transition 设置 150--250ms 的时长,避免过慢导致操作迟滞。

8. 小结

伪类 触发条件 典型用途
:hover 鼠标悬停 按钮高亮、卡片浮起、行高亮
:focus 元素获得焦点 输入框边框、按钮焦点态
:focus-visible 需要显示焦点指示时 键盘导航的焦点轮廓
:focus-within 自身或后代获得焦点 表单容器整体高亮

:hover 与 :focus 是构建交互反馈的基石。用好它们的关键在于:既要有明确的视觉反馈,又不能牺牲可访问性 。配合 :focus-visible、:focus-within 以及媒体查询,可以写出既美观又对键盘与触摸用户友好的样式。

相关推荐
风骏时光牛马1 小时前
业务基础信息管理库
前端
zyxqyy&∞1 小时前
sqli-labs-布尔盲注 (Less-8),时间盲注 (Less-9~10)
前端
m0_敲代码的彭于晏1688 小时前
Vercel 前端部署平台深度评测
前端
怕浪猫10 小时前
外包和大厂的前端代码,差在凌晨三点
前端·面试·github
m0_敲代码的彭于晏16810 小时前
Vercel 前端部署与全栈开发实战指南
前端·状态模式
徐小夕10 小时前
花了3个月,我们做了一款 AI 版的“飞书文档”
前端·算法·github
贾伟康10 小时前
【化学实验室|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂
前端·移动开发·harmonyos·arkts·arkui
Csvn11 小时前
SSR/SSG/ISR(上):峡谷与渲染策略的时间预算
前端
Csvn11 小时前
SSR/SSG/ISR(下):可交互性、新鲜度与回源账
前端