用 :has() 实现“悬停一个,其他变暗”的核心是父容器上使用 .cards:has(.card:hover) .card:not(:hover) 选择器,在支持浏览器中使非悬停卡片变暗,不支持时自动降级。

用 :has() 实现“悬停一个,其他变暗”的核心逻辑
这功能的关键不是给 hovered 元素加样式,而是用 :has() 反向选中它的兄弟元素——现代 CSS 中唯一能“向上/向外”影响兄弟的伪类。必须确保容器是同一父级,且浏览器支持(Chrome 105+、Safari 15.4+、Firefox 103+)。不支持时整个规则会被忽略,不会报错,但效果消失。
:has() 的选择器写法和常见错误
错误写法:.card:hover ~ .card —— 这只是普通兄弟选择器,不涉及 :has(),也无法触发“悬停时影响其他”的条件逻辑。正确路径是:在父容器上用 :has() 检测某个子元素是否被悬停,再对其他子元素设样式。
- ✅ 正确:
.cards:has(.card:hover) .card:not(:hover) - ❌ 错误:
.card:has(:hover)——:has()不能写在被悬停的元素自身上,它必须挂载在有兄弟关系的祖先上 - ⚠️ 注意:
:not(:hover)必须写在具体要变暗的元素上,不能只靠:has()判断就完事
实际可用的 CSS 片段与兼容性处理
以下代码在支持 :has() 的浏览器中生效;旧浏览器会跳过整条规则,卡片保持默认状态(无暗淡),属于优雅降级:
.cards {
display: flex;
gap: 1rem;
}
.cards:has(.card:hover) .card:not(:hover) {
opacity: 0.4;
transition: opacity 0.2s ease;
}
.card:hover {
z-index: 2;
}
如果你用的是 Grid 布局或需要保留 pointer-events,注意:opacity: 0.4 不会影响事件捕获,所以其他卡片仍可点击;若想禁用交互,得额外加 pointer-events: none,但这样悬停离开后可能无法立刻恢复,建议优先用 opacity 配合 transition。
为什么不能用 JavaScript 简单替代?
能替代,但没必要——这个场景纯 CSS 更轻量、无 JS 加载延迟、无事件监听开销。不过要注意:如果卡片是动态插入的(比如通过 innerHTML 或 React 渲染),:has() 会实时响应,无需手动绑定;但若用 JS 手动切换 class,反而容易破坏响应性或引发竞态。真正容易被忽略的是父容器的 display 类型:Flex/Grid 容器下兄弟选择器才可靠,而 float 或 inline-block 布局可能导致 ~ 失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











