:focus-visible 的真实行为边界是浏览器根据用户交互历史动态判断是否启用,而非仅响应键盘;它默认不匹配鼠标点击焦点,但存在延迟和例外,需配合 :focus 和 :focus-within 分层控制样式。

什么是 :focus-visible 的真实行为边界
:focus-visible 不是“只响应键盘”的开关,而是浏览器根据用户交互历史(比如是否刚按过 Tab、是否有鼠标移动)动态判断是否启用该伪类。它默认不匹配鼠标点击获得焦点的元素——但这个判断有延迟和例外:比如用触控板点按、辅助工具触发、或某些浏览器在页面加载后首次点击,仍可能短暂触发 :focus-visible。
正确覆盖鼠标点击时的 outline 显示
不能只依赖 :focus-visible,必须配合 :focus 和 :focus-within 做分层控制。核心策略是:先全局移除 :focus 的 outline,再仅对 :focus-visible 单独加回。
- 在重置样式中写:
*:focus { outline: none; } - 再单独恢复键盘焦点样式:
*:focus-visible { outline: 2px solid #007aff; outline-offset: 2px; } - 注意:不要用
!important,否则会干扰高对比度模式下的系统 outline 覆盖
Chrome / Safari 中的兼容性陷阱
:focus-visible 在 Chrome 86+ 和 Safari 15.4+ 支持,但 Safari 对「鼠标点击后是否触发」更保守;而旧版 Edge(基于 EdgeHTML)完全不支持。若需降级,可用 focus-visible polyfill,但它会监听 mousedown 事件并动态加 class,可能与 React 的合成事件冲突。
实现 Markdown↔ HTML 互转,支持导出 PDF,用于用户请求的文档格式转换。
- 检测是否支持:
if (CSS.supports('selector(:focus-visible)')) { /* 启用原生逻辑 */ } - polyfill 初始化必须在 DOM ready 后执行,且不能在 Shadow DOM 内自动生效
- Firefox 默认不启用该伪类(需手动开启
layout.css.focus-visible.enabled),实际项目中建议忽略 Firefox 的原生行为,统一走 polyfill
按钮、输入框、自定义组件的差异化处理
原生 <button></button> 和 <input> 默认支持 :focus-visible,但自定义组件(如用 div role="button")必须显式添加 tabindex="0" 才能获得焦点能力,且需要确保没有 outline: none 提前覆盖了所有焦点状态。
立即学习“前端免费学习笔记(深入)”;
- 可聚焦容器内嵌链接时,
:focus-within可能意外激活,应避免对整个容器设:focus-visible - 禁用状态(
disabled)下:focus-visible永远不匹配,无需额外处理 - 使用
appearance: none的<select></select>需手动加 outline,因为部分浏览器会丢弃其原生焦点样式
真正难的是让视觉反馈既符合 WCAG 2.4.7(焦点可见性),又不干扰鼠标用户的操作流。很多团队卡在 Safari 下首次点击仍闪 outline,其实是因为 polyfill 初始化太晚——得在 里同步加载并立即执行初始化逻辑。


















