:focus-within可让父容器在子元素获得焦点时保持样式,但需子元素可聚焦(如加tabindex="0"),且不替代js状态管理。

点击后样式消失是因为:focus只作用于自身
直接给按钮加 :focus,点完松手就失效——浏览器默认只在获得焦点且未失焦时维持该伪类。想让父容器“记住”点击状态,:focus 单独用不行,必须靠事件或更聪明的 CSS 机制。
:focus-within 要求子元素能获取焦点
:focus-within 是个守门员:它不关心自己有没有被点,只看内部有没有任意一个可聚焦元素处于 :focus 状态。所以父元素要“留住”点击效果,得满足两个条件:
- 父元素本身不能设
tabindex="-1"或禁用聚焦(否则子元素即使聚焦,父级也收不到冒泡信号) - 子元素必须是天然可聚焦的(如
<button></button>、<a href></a>),或显式加tabindex="0"
常见翻车点:把 <div> 当按钮用,又没加 <code>tabindex="0",:focus-within 就完全不触发。
配合 tabindex 和 outline 的最小可行方案
以下代码能让点击后父容器保持背景色,直到用户点别的地方:
<div class="card"> <button tabindex="0">展开</button> </div>
.card:focus-within { background: #f0f8ff; }
注意三点:
- 按钮必须有
tabindex="0"(哪怕它本来就是可聚焦元素,显式声明更稳) - 如果按钮有
outline: none,记得补上视觉反馈,否则键盘用户无法感知焦点位置 - 移动端 Safari 对
:focus-within的支持从 iOS 15.4 才稳定,旧版本需降级用 JS 监听click+ 切换 class
别指望 :focus-within 替代 JavaScript 状态管理
它只是 CSS 层的“临时快照”,不是真状态。比如:
- 刷新页面后样式必然丢失
- 无法区分是点击还是 Tab 进入焦点
- 不能和表单提交、异步加载等逻辑联动
真正需要持久化状态(比如折叠面板展开后不随刷新重置),还是得用 localStorage 或框架的响应式状态。CSS 只负责“此刻看起来像被点过”。











