只写 :focus-visible 会丢失鼠标反馈,因其仅在键盘导航时触发;Safari 15.3 及更早版本忽略该规则,故必须前置 :focus 作兼容兜底,且顺序不可颠倒。

为什么只写 :focus-visible 样式会彻底丢失鼠标反馈
浏览器只在明确的键盘导航行为(Tab、Shift+Tab、方向键、Enter、Space)下才匹配 :focus-visible;鼠标点击、触摸 tap、甚至 JS 调用 el.focus() 都不会触发它。所以如果只写 button:focus-visible { outline: 2px solid #0066cc; },鼠标用户点按钮后页面毫无视觉变化——不是 bug,是设计如此。
更关键的是兼容性:Safari 15.3 及更早版本会直接忽略整条 :focus-visible 规则;如果没有前置的 :focus 声明,这部分用户连基础焦点提示都没有。
-
:focus是兜底层,必须存在且写在前面 -
:focus-visible是增强层,仅用于键盘用户高亮 - 顺序颠倒(
:focus-visible在前)会导致旧浏览器里焦点样式完全消失
button:focus 和 button:focus-visible 的正确写法顺序
必须先声明 :focus 提供基础反馈,再用 :focus-visible 覆盖或增强。错误顺序或全局清 outline 会让整个逻辑崩掉。
- ✅ 正确:
button:focus { outline: 2px solid #999; outline-offset: 2px; }→button:focus-visible { outline-color: #0066cc; } - ❌ 错误:
button:focus-visible { outline: ... }写在:focus前面 - ❌ 危险:
* { outline: none; }会干掉所有:focus和:focus-visible的 outline,除非显式重置 - ⚠️ 注意:
outline-offset至少设为2px,否则圆角按钮边缘容易粘连
哪些元素默认不触发 :focus-visible?怎么补救
不是加了 tabindex="0" 就自动生效。浏览器根据语义和交互意图判断是否启用该伪类,尤其对自定义组件非常敏感。
立即学习“前端免费学习笔记(深入)”;
- ✅ 默认支持:
<button>、<input>、<textarea>、<select>、带tabindex="0"的<a href> - ❌ 默认不触发:
<div role="button">(缺keydown监听)、<span tabindex="0">(无 role 或事件)、禁用态(disabled或aria-disabled="true") - ? 补救方法:
<div role="button" tabindex="0">必须监听keydown,并在event.key === 'Enter' || event.key === ' '时触发对应操作 - ? 验证技巧:在 DevTools 中手动勾选元素的
:focus-visible状态,比反复切键盘更快确认样式是否生效
Safari 和移动端上容易被忽略的实际表现
Safari 15.4+ 支持 :focus-visible,但对触控板点按或首次点击仍可能短暂触发;Chrome/Edge/Firefox 表现稳定,但以下情况依然失效:
- 用了
autofocus属性——自动聚焦不算键盘导航,:focus-visible不匹配 - JS 调用
el.focus()未传{ focusVisible: true }(仅 Chromium 支持该选项) - 元素被
display: none或visibility: hidden拦截,即使有tabindex="0"也无效 - React/Vue 封装组件中,焦点常被父容器劫持,需确保底层 DOM 元素真正接收了焦点
最稳妥的做法是在真实设备上走一遍 Tab 导航流程,别只靠桌面模拟器验证。复杂点不在 CSS 写法本身,而在交互路径是否真实贯通。


















