:focus触发条件宽泛,鼠标点击也会激活导致视觉干扰;:focus-visible仅在键盘导航时生效,需与:focus分层使用、顺序正确,并注意元素支持范围及浏览器兼容性。

focus触发条件太宽泛,容易干扰鼠标用户
:focus只要元素获得焦点就生效,不管你是用Tab键切换、鼠标点击、触摸屏点按,还是JS调用element.focus()——全算。结果就是鼠标点一下按钮,蓝框突然弹出,纯属视觉干扰;而键盘用户又确实需要这个反馈。这不是设计缺陷,是语义本身没区分“怎么聚焦的”。
常见错误现象包括:* { outline: none }全局清空后,键盘用户彻底失去焦点提示;或者只写button:focus { outline: 2px solid blue },导致鼠标点按钮时边框跳闪,尤其在表单密集页很刺眼。
使用场景上,:focus仍是兜底必需项:输入框获得焦点时要高亮边框、光标要闪烁、下拉菜单要展开——这些和交互方式无关,必须响应。
focus-visible只认键盘导航,但默认不覆盖:focus
:focus-visible不是:focus的升级版,而是浏览器根据交互意图做的二次判断:只有Tab、Shift+Tab、方向键、Enter、Space这类明确的键盘行为才会触发它。鼠标点、触摸tap、甚至element.focus()默认都不算。
立即学习“前端免费学习笔记(深入)”;
关键点在于:它不会自动替代:focus,必须显式声明样式,且顺序不能错:
- 先写
input:focus { outline: none; }(为鼠标/触摸用户收掉默认蓝框) - 再写
input:focus-visible { outline: 2px solid #0066cc; outline-offset: 2px; }(仅给键盘用户加可感知轮廓)
如果反过来,旧浏览器会直接忽略:focus-visible整条规则,而:focus又被压在后面,结果是焦点完全不可见。
哪些元素能用:focus-visible?不是加tabindex=0就行
浏览器有内置白名单:<input>、<textarea></textarea>、<button></button>、<select></select>天然支持;带tabindex="0"的<div>也行。但以下情况默认不触发:<ul>
<li>
<code><a></a>没href属性 → 即使加tabindex="0"也不行
<label></label>没设tabindex → 不进键盘流<div role="button">没监听<code>keydown事件 → 浏览器认为它不可键盘操作自定义组件想用:focus-visible,得同时满足:语义正确(role)、可聚焦(tabindex)、可键盘响应(onkeydown处理Enter/Space)——三者缺一不可。
兼容性和调试必须手动验证,不能靠猜
:focus-visible在Chrome 86+、Firefox 89+、Safari 15.4+原生支持;Safari 15.3及更早版本直接忽略整条规则,且@supports selector(:focus-visible)在这些旧版里会解析失败,导致降级样式也丢掉。
最省事的做法是引入focus-visible polyfill——它检测到原生支持就静默退出,不支持时自动注入逻辑。调试时别反复切键盘,直接在DevTools Elements面板勾选元素的:focus-visible状态,看样式是否生效。另外注意:element.focus({ focusVisible: true })是Chrome 111+才支持的强制触发方式,别在旧环境硬用。
最容易被忽略的是:页面加载后JS自动聚焦某个输入框,:focus-visible默认不匹配——用户没按任何键,浏览器不认为这是“键盘导航起点”。这时候要么接受无轮廓,要么用focus({ focusVisible: true })补救(前提是目标环境支持)。


















