必须用 js-focus-visible Polyfill 适配旧浏览器,因其体积小、兼容性好,能准确区分键盘/鼠标聚焦;手动模拟易出错且难覆盖全场景。

旧版 Chrome(86 以下)、IE、部分企业定制浏览器根本不认识 :focus-visible,直接忽略整条 CSS 规则——这不是写法错,是语法不支持。必须用 Polyfill 补上逻辑,否则键盘用户根本看不到焦点。
为什么不能只用 :focus fallback?
直接把 :focus-visible 替成 :focus,会导致鼠标点按钮、链接时也弹出 outline,干扰视觉动效,还违背“仅键盘用户需要提示”的可访问性原则。更关键的是::focus 和 :focus-visible 语义完全不同——前者只管“有没有焦点”,后者管“怎么获得的焦点”。混用等于放弃语义控制。
js-focus-visible 是唯一靠谱的 Polyfill 方案
它体积小(约 2KB gzip),不依赖 MutationObserver,从 IE11 到 Chrome 49 都能跑。核心逻辑是:监听首次键盘操作(Tab、Shift+Tab、方向键等),在 上加 js-focus-visible 类,并给当前聚焦元素加 focus-visible 类。
- 引入方式:在
或底部加<script src="https://unpkg.com/js-focus-visible@5.0.0/dist/js-focus-visible.min.js"></script> - CSS 必须改写为类选择器,例如:
button:focus-visible, button.js-focus-visible .focus-visible { outline: 2px solid #007aff; } - 必须加降级规则:
button:focus:not(.js-focus-visible .focus-visible) { outline: none; },否则旧版 Chrome 会回退到默认 outline - 若用 Webpack/Vite,建议
import 'js-focus-visible'并配置sideEffects: false,避免被 tree-shaking 误删
手动模拟 :focus-visible 的坑比你想象的多
有人想自己监听 keydown + focus 打标,但几乎必踩坑:
立即学习“前端免费学习笔记(深入)”;
-
keydown后要过滤非导航键(比如Enter、Space在表单中是合法触发,但纯输入场景不该算) -
focus事件本身不带来源信息,单靠它无法区分鼠标 click 和键盘 Tab;必须结合mousedown/keydown时序判断,逻辑复杂 - 遇到
iframe或 Shadow DOM,事件捕获链断裂,手动逻辑大概率失效 - 移动端 Safari 在 JS 调用
.focus()且无真实用户手势时,压根不触发focus事件——Polyfill 内部已处理,手写很难覆盖全
真正难的不是加一行 <script></script>,而是确保所有可聚焦元素(包括 tabindex="0" 的 <div>、封装过的 React Button、自定义 Web Component)都被 Polyfill 正确识别。这点容易被忽略,但一旦漏掉,键盘用户就彻底失焦了。</div>


















