:focus-visible单独写无效,因它仅在Tab/Enter等键盘导航时触发,鼠标点击、JS聚焦均不匹配;且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——这是正确行为,别强行绕过 - 如果确实需要让 JS 聚焦触发视觉反馈,改用
element.focus({ preventScroll: true, focusVisible: true })(Chrome 106+、Firefox 117+ 支持) - 旧版浏览器需 fallback 到手动添加临时类,例如
element.classList.add('js-focus-visible'),并配 CSS.js-focus-visible { outline: 2px solid #007aff; } - 注意:不要在所有
focus()调用中无差别加focusVisible: true,它会破坏原生的“仅键盘触发”语义
JS 调用 focus() 后:focus-visible 仍不生效怎么办
页面加载后 JS 调用 element.focus() 默认不算键盘导航,:focus-visible 不匹配。这是最隐蔽也最容易被忽略的一环。
真正难的不是写对那两行 CSS,而是理解浏览器何时决定“这个焦点值得被看见”。很多团队卡在自定义组件上,因为它们既没语义又缺键盘事件监听,:focus-visible 直接拒之门外——这时候补的不是样式,是交互意图的表达。


















