:focus伪类不生效的根本原因是元素未真正获得焦点或样式被覆盖;需确保元素可聚焦(如加tabindex="0")、检查父容器样式、验证activeElement,并优先使用:focus-visible配合降级策略。

根本原因不是代码写错了,而是元素没真正获得焦点,或:focus样式被默认行为覆盖、压制。
元素本身不可聚焦,:focus自然不触发
像 div、span、p 这类非表单元素,默认无法接收焦点,哪怕写了 div:focus 也永远不会生效。浏览器只对“可聚焦且当前拥有焦点”的元素应用该伪类。
- 给元素加
tabindex="0"是最直接的解法(tabindex="-1"仅支持 JS 主动聚焦,不能键盘 Tab 切入) - 检查父容器是否用了
pointer-events: none或visibility: hidden—— 这些会阻断聚焦链 -
display: none的元素完全脱离渲染树,调用.focus()也无效;得先确保它已挂载且display不为none - 在控制台执行
document.activeElement,确认目标元素是否真在焦点中
:focus 样式被 outline 覆盖或重置
你写的 input:focus { outline: 2px solid #007bff; } 看似生效,但实际可能被浏览器默认 outline 叠加、或被全局 * { outline: none; } 干掉。
- 打开开发者工具的 Computed 面板,搜索
outline,看最终值是不是none或被其他规则覆盖 - 不要只依赖
outline:它在部分系统设置下会被强制隐藏,且不支持圆角;改用box-shadow更可控 - 推荐写法:
input:focus { outline: none; box-shadow: 0 0 0 3px rgba(0, 123, 191, 0.25); } - 若项目用了 CSS reset/normalize,可能清掉了
input的默认border,需手动补上:input { border: 1px solid #ced4da; }
:focus-visible 才是区分交互方式的关键
直接强化 :focus 会导致鼠标点击后也出现高亮,违背“仅键盘用户需要强视觉反馈”的设计逻辑。真正该用的是 :focus-visible,但它不能单独存在。
立即学习“前端免费学习笔记(深入)”;
-
:focus-visible只在浏览器判定为键盘导航(Tab/Shift+Tab/Enter)时匹配,Safari 15.4+、Chrome/Firefox 均已原生支持 - 必须按顺序写:
input:focus(兜底基础反馈)→input:focus:not(:focus-visible)(清除鼠标点击的轮廓)→input:focus-visible(强化键盘焦点) - 错误写法:
input:focus-visible { outline: none; }—— 鼠标点击时:focus-visible根本不匹配,这行等于没写 - 自定义组件(如 React 封装的 Input)若未透传
focusOptions或未设role,:focus-visible很可能静默失效
iOS Safari 和 Shadow DOM 是两个隐蔽高发区
真机测试常暴露问题:iOS Safari 对 input 的焦点样式有额外限制;Shadow DOM 内部的样式作用域又会切断外部 :focus 规则。
- iOS 必须有正确的
<meta name="viewport" content="width=device-width, initial-scale=1">,否则焦点样式可能被禁用 - 给
input加cursor: pointer或touch-action: manipulation可提升响应可靠性 - Shadow DOM 中,外部样式表里的
input:focus默认不生效;得把样式写进组件内部的<style>,或用::slotted(input:focus)(仅限 slot 内容) - 移动端
input[type="number"]在 iOS 上焦点延迟明显,建议统一用type="text"+ 输入限制
最常被忽略的一点::focus-visible 的触发依赖浏览器 UA 启发式判断,不是稳定开关。页面首次交互是鼠标时,后续一段时间内它可能“降级”为全走 :focus;autofocus 也不算键盘导航,不会触发它。别指望一次声明就一劳永逸,得结合真机测试和降级逻辑来兜底。


















