:focus-visible没反应不是代码错误,而是浏览器未判定聚焦“该被看见”;它仅响应tab/shift+tab/方向键/enter/space等键盘导航,鼠标点击、js focus()、tabindex="-1"首次tab、全局outline:none均会导致失效,必须与:focus共存且:focus声明在前。

为什么:focus-visible写了却没反应
不是代码写错了,而是浏览器根本没判定这次聚焦“该被看见”。常见触发失败场景包括:鼠标点击后按空格/回车,焦点来源仍是鼠标,:focus-visible不匹配;tabindex="-1"的元素首次被 Tab 进入,Safari 可能拒绝激活;JS 调用 element.focus() 默认不算键盘导航;全局写了 * { outline: none; },连 :focus-visible 的基础样式也被清掉。调试时先运行 console.log(document.activeElement),确认焦点真落在目标上,且它的 tabIndex ≥ 0、未被 disabled。
必须和:focus一起用,且顺序不能错
:focus-visible 不能单独存在——只写它,鼠标点按钮就完全没反馈,用户会怀疑操作失效;旧版 Safari(15.3 及更早)直接忽略整条规则,结果是所有浏览器都看不到焦点。正确写法必须是:
button:focus { outline: none; }
button:focus-visible { outline: 2px solid #0066cc; outline-offset: 2px; }
这样做的效果是:老浏览器只执行第一行,移除所有焦点框;新浏览器在键盘 Tab 进入时恢复轮廓,鼠标点击则保持无边框。如果把 :focus-visible 写在前面,或加 !important 强顶,降级逻辑就断了。
哪些元素默认支持:focus-visible
不是加了 tabindex="0" 就自动生效。浏览器按语义和交互模式内置判断:
- ✅ 默认触发:
<input>、<textarea></textarea>、<button></button>、<select></select>、带tabindex="0"的<div><li>❌ 默认不触发:<code><a></a>(无href)、<label></label>(未设tabindex)、<div role="button">(缺 <code>keydown监听)自定义控件如
div[role="button"],光加tabindex="0"不够,还必须监听Enter和Space并调用click(),否则部分浏览器拒绝激活:focus-visible。编辑器、工具栏等复杂组件怎么加
这类场景最容易踩坑,因为焦点路径绕、交互混合:
- 工具栏按钮:必须设
tabindex="0",并监听keydown响应Enter/Space -
contenteditable容器本身不触发:focus-visible,要给它加tabindex="0"+focusin事件,再用 JS 切换.focus-visible类来控制边框高亮 - 下拉菜单浮层:根节点用
tabindex="-1",打开后 JS 调用.focus();内部选项用 roving tabindex(仅一个tabindex="0"),每个都要支持:focus-visible - 禁用状态按钮:即使加了
tabindex="0",也得配合aria-disabled="true"并确保不响应focus,否则键盘用户会卡在不可用项上
最常被忽略的是
outline-offset: 2px——工具栏图标密集,紧贴边框的轮廓容易粘连,呼吸感一丢,可读性立刻垮掉。 - 工具栏按钮:必须设











