<p>父容器hover时所有子项变暗、被悬停子项恢复亮度,需用.parent:hover > { opacity: 0.3; }配合.parent > :hover { opacity: 1; }实现,禁用pointer-events: none防闪退,统一transition: opacity 0.25s ease-out。</p>

父容器 hover 时让所有子项变暗,但被悬停的那个恢复亮度
直接写 .parent:hover > *:not(:hover) { opacity: 0.3; } 是无效的——:not(:hover) 不会响应“别的元素 hover”,它只判断自身是否处于 hover 状态。真正能落地的做法是:把暗淡态设在父容器上,再用子选择器单独“提亮”当前悬停项。
常见错误现象:
-
.parent > *:hover ~ * { opacity: 0.3; }——~只能选后续兄弟,漏掉前面的; - 给每个子项加
transition但没统一 duration 和 timing-function,导致闪烁或不同步; - 忘记设
pointer-events: none,鼠标从一个子项移到另一个时,父级 hover 状态中断,造成闪退。
实操建议:
- 父容器初始不设 opacity,所有子项默认
opacity: 1; - 加
.parent:hover > * { opacity: 0.3; },让全部子项变暗; - 再写
.parent > *:hover { opacity: 1; },覆盖被悬停的那个; - 所有子项必须声明
pointer-events: none,仅被 hover 的那个显式设回pointer-events: auto(否则移出瞬间会丢失 hover 状态); - 过渡统一加在父容器或子项上:
transition: opacity 0.25s ease-out,别分散写。
用 :has() 精准控制 grid-area 悬停时兄弟区域变暗
如果你用 grid-template-areas 定义了 "header main sidebar",想悬停 .main 时只暗淡 .header 和 .sidebar,:has() 是目前最干净的纯 CSS 解法(Chrome 105+、Safari 15.4+、Firefox 121+ 支持)。
立即学习“前端免费学习笔记(深入)”;
不能写成 .grid:has(.main:hover) > * —— 这会把所有子项都变暗,包括 .main 自己。必须排除它:
.grid:has(.main:hover) .header, .grid:has(.main:hover) .sidebar { opacity: 0.25; }- 每个区域都要单独列规则,没法用通配符替代;
- 区域超过 5 个时,
:has()规则叠加会导致性能下降,建议降级为 JS 添加data-hovered="main"类来驱动样式; - 不支持
:has()的浏览器会完全忽略这些规则,效果静默失效,无报错也无 fallback。
为什么鼠标移到子项边缘就突然全变暗?
这是 hover 区域断层导致的:父容器的 :hover 范围和子项自身的 :hover 范围之间存在微小间隙(比如子项有 margin、父容器有 border 或未填满的 padding),鼠标一移出父级边界,父级样式立刻撤掉,而子项还没来得及捕获 hover,整个视觉就闪一下全暗。
排查与修复要点:
- 检查子项是否有
margin—— 它不属于父容器的 hover 区域,应改用padding或负 margin 补齐; - 确认父容器没有
overflow: hidden切掉子项的悬停热区; - 用浏览器开发者工具的「:hover 强制状态」功能,逐个测试每个子项的可 hover 区域是否连续;
- 如果用了框架(如 Ant Design),留意其组件是否自带
pointer-events: none或user-select: none,干扰了原生 hover 检测。
移动端 hover 效果为何不触发或只点一次才生效?
iOS Safari 和部分安卓 WebView 默认禁用持续 hover,这是为触摸设备做的主动优化,不是 bug。它只在第一次点击后临时启用 hover 状态,之后又可能失效。
不要依赖 hover 做关键交互(比如隐藏操作按钮),若必须保留 PC 端效果,要用媒体查询隔离:
@media (hover: hover) { .list:hover > .item:not(:hover) { opacity: 0.4; } }- 这个规则只在支持 hover 的设备上生效,触摸设备完全跳过;
- 别用
@media (hover: none)去写 fallback 样式——它匹配的是“永远不支持 hover”的设备,但多数触摸屏其实是“有条件支持”,容易误判; - 真要强兼容,就得用 JavaScript 监听
mouseenter/mouseleave,并配合touchstart做 polyfill。
最易被忽略的一点:opacity 变化本身不触发重排,但如果你同时动了 height、margin 或 display,动画就会卡顿甚至失效。只靠 opacity + transition 是最稳的路径。


















