:focus-visible 是目前最可靠的方式,但它不能直接替换 :focus,必须共存并按顺序书写;:focus 必须在前重置样式,:focus-visible 在后覆盖,以确保键盘导航有焦点提示、鼠标操作无干扰,且兼容不支持该伪类的浏览器。

:focus-visible 是目前最可靠的方式,但它不能直接替换 :focus,必须共存并按顺序书写。 直接删掉 :focus、只留 :focus-visible 会导致鼠标点击后元素完全无焦点反馈,用户会误以为操作没生效。
为什么 :focus-visible 比 :focus 更适合键盘导航
浏览器对 :focus 的处理是“一视同仁”:鼠标点、触摸点、Tab 切换,只要元素获得焦点,:focus 就匹配。这导致 button:focus { outline: 2px solid blue; } 在鼠标点击瞬间也弹出蓝边,干扰视觉,且 Safari 中常残留不消失。
:focus-visible 则由浏览器原生判断输入方式——只有 Tab、Shift+Tab、方向键、Enter、Space 等明确的键盘导航行为触发时才生效。鼠标/触摸操作不会激活它,因此不会出现“莫名其妙的轮廓线”。
- 不是 bug,是规范行为;:focus 的设计本就不是为区分输入源
- 无障碍用户依赖可见焦点路径,而鼠标用户根本不需要这个提示
- Chrome 86+、Firefox 84+、Edge 89+、Safari 15.4+ 原生支持(但 Safari 15.0–15.3 需手动开启辅助功能才启用)
安全写法::focus 必须在前,:focus-visible 在后
顺序错了,降级逻辑就崩了。浏览器不支持 :focus-visible 时会整条规则忽略,如果 :focus 写在后面,它会被前面的 :focus-visible 覆盖不到,结果就是所有浏览器都看不到焦点样式。
正确示例:
button:focus {
outline: none;
}
button:focus-visible {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
- 永远先重置 :focus(窄范围、低权重),再用 :focus-visible 覆盖它
- 避免用
!important强行覆盖,靠声明顺序和选择器权重自然接管 - 如果全局写了
* { outline: none; },必须显式为:focus-visible恢复outline,否则它也被清掉了
不支持时怎么降级?别用 @supports selector()
Safari 15.3 及更早版本解析 @supports selector(:focus-visible) 会失败,导致整个 CSS 块被丢弃——你连降级样式都拿不到。
真正可行的降级路径只有两个:
- 引入
focus-visiblepolyfill(如npm install focus-visible或 CDN:<script src="https://unpkg.com/focus-visible"></script>),它在支持原生特性时静默退出,不支持时自动接管 - 手写 JS 检测:监听
keydown设置body.js-focus-visible类,再写.js-focus-visible button:focus { outline: 2px solid #0066cc; } - 切勿用
:focus:not(:focus-visible)反向隐藏——不支持的浏览器里,:focus-visible失效,:focus:not(:focus-visible)就变成:focus,结果所有焦点都被隐藏
最容易被忽略的是移动端真机行为:Safari 在首次触摸后进入“指针模式”,哪怕之后按 Tab 也不触发 :focus-visible,直到刷新或用户手动按一次 Tab 进入键盘模式。这类边界情况必须用真机测试,模拟器不可靠。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











