:focus-visible在旧版浏览器中完全无效,因其css解析器直接忽略该伪类;必须使用js-focus-visible polyfill并配合类选择器重写样式,且需注意加载顺序、基础:focus保障及自定义元素的可访问性配置。

为什么:focus-visible在旧版浏览器里完全没反应
旧版浏览器(Chrome 86 以下、Safari 15.3 及更早、IE 全系、部分企业内核)压根不识别 :focus-visible 这个伪类——CSS 解析器直接跳过整条规则,连警告都不报。这不是你写错了选择器,而是语法层面不存在。用户鼠标点按钮没反馈、键盘 Tab 时看不到焦点框,问题就出在这儿。
必须用 js-focus-visible,别手写判断逻辑
有人想监听 keydown + focus 手动打标,但几乎必踩坑:
-
focus事件本身不带来源信息,单靠它无法区分鼠标 click 和键盘 Tab - 遇到 iframe 或 Shadow DOM,事件捕获链断裂,逻辑直接失效
- 移动端 Safari 在 JS 调用
.focus()且无真实用户手势时,压根不触发focus事件 - key 事件要过滤非导航键(比如输入框里的 Enter 不该算“导航触发”)
真正能覆盖全场景的只有 js-focus-visible:体积小(约 2KB gzip),兼容 IE11 到 Chrome 49,核心逻辑是监听首次键盘操作(Tab/Shift+Tab/方向键),在 上加 js-focus-visible 类,并给当前聚焦元素加 focus-visible 类。
CSS 必须重写为类选择器 + 降级规则
引入 Polyfill 后,CSS 不能继续用原生伪类写法。必须改成:
button:focus:not(.js-focus-visible .focus-visible) {
outline: none;
}
button:focus-visible,
button.js-focus-visible .focus-visible {
outline: 2px solid #007aff;
outline-offset: 2px;
}
注意三点:
-
button:focus:not(.js-focus-visible .focus-visible)必须写在前面,否则旧浏览器会因忽略:focus-visible而执行后面的outline: none,导致所有焦点都消失 - 如果全局写了
*:focus { outline: none; },那:focus-visible就彻底失去作用基础——它需要先有:focus才能叠加判断 - 自定义元素(如
<div tabindex="0">)必须同时满足:<code>tabindex="0"、明确role(如role="button")、监听onkeydown并处理 Enter/Space,否则 Polyfill 不会把它纳入判断范围别依赖 @supports 包裹 :focus-visible 样式
@supports selector(:focus-visible)在 Safari 15.3 及更早版本中会被整个丢弃——不是只忽略内部规则,而是连@supports块都跳过。结果是:连兜底的:focus样式也失效。安全路径只有一条:先写:focus(基础保障),再用同权重的:focus-visible或类选择器覆盖(增强判断)。Safari 对该伪类的启用还依赖系统辅助功能开关,macOS/iOS 上若未开启“按下 Tab 键以在网页上进行键盘导航”,即使版本达标也可能不生效。











