:focus-visible仅在Tab、Shift+Tab、方向键、Enter、Space等键盘导航时触发,鼠标点击、触摸及JS focus()均不匹配;Safari 15.3及更早版本直接忽略该规则,必须用:focus兜底且声明在前,否则无焦点反馈。

为什么只写 :focus-visible 没反应
浏览器只在 Tab、Shift+Tab、方向键、Enter、Space 这类键盘导航行为下匹配 :focus-visible;鼠标点击、触摸、甚至 JS 调用 element.focus() 都不会触发它。所以如果你只写 button:focus-visible { outline: 2px solid #007aff; },鼠标点按钮后完全没反馈——这不是 bug,是设计如此。
更关键的是兼容性:Safari 15.3 及更早版本会直接忽略整条 :focus-visible 规则。没兜底,就等于全军覆没。
- 别用
*:focus { outline: none; },它会把:focus-visible的基础能力一起干掉 - 必须显式设置可感知样式,比如
outline或box-shadow,不能只依赖浏览器默认 - DevTools 里手动勾选元素的
:focus-visible状态,比反复切键盘更快验证是否生效
:focus 和 :focus-visible 的声明顺序怎么写
:focus 是兜底层,:focus-visible 是增强层,二者不是替代关系,而是降级路径。顺序错了,旧浏览器就看不到任何焦点提示。
- ✅ 正确写法:
button:focus { outline: 1px dotted #999; }在前,button:focus-visible { outline: 2px solid #007aff; outline-offset: 2px; }在后 - ❌ 错误写法:
:focus-visible写在:focus前面;或两者都加!important导致层叠混乱 -
outline-offset: 2px很关键——工具栏图标间距常小于 4px,不加容易和相邻元素粘连
哪些元素默认不触发 :focus-visible,怎么补救
不是加了 tabindex="0" 就自动支持。浏览器根据语义和交互意图判断是否启用该伪类,常见“假可聚焦”场景需要额外处理:
立即学习“前端免费学习笔记(深入)”;
<div role="button">:必须监听 <code>keydown,显式响应 Enter 和 Space,否则 Chromium 可能判定为“非交互意图”- 自定义
contenteditable容器:它自身不触发:focus-visible,需手动加tabindex="0",并在focusin中用 JS 切换.focus-visible类模拟 -
<select></select>用了appearance: none:部分浏览器会丢弃原生焦点样式,得手动给select:focus-visible补outline - 禁用态(
aria-disabled="true"或disabled):即使有tabindex="0",也不会匹配:focus-visible——这是正确行为,别强行绕过 - Chrome 111+ 支持
element.focus({ focusVisible: true })强制触发 - 旧版浏览器只能靠 fallback:在 JS 聚焦后,手动添加临时类(如
.js-focus-visible),并用 CSS 模拟样式 - 不要依赖
@supports selector(:focus-visible)包裹整个规则块——Safari 15.3 及更早版本解析失败会导致整块 CSS 被丢弃
JS 聚焦时 :focus-visible 不生效怎么办
页面加载后 JS 调用 element.focus() 默认不算键盘导航,:focus-visible 不匹配。这是最隐蔽也最容易被忽略的一环。
真正容易被忽略的点在于:自定义组件中,tabindex="0" + role + 键盘事件监听这三者缺一不可;少一个,:focus-visible 就可能静默失效,且很难复现和调试。


















