本文介绍如何利用现代 CSS 的 :has() 伪类解决“父级 <li> 悬停时无法控制同级(非嵌套)子菜单显示”的常见布局难题,并提供完整可运行代码与兼容性注意事项。
本文介绍如何利用现代 css 的 `:has()` 伪类解决“父级 `
在构建响应式导航菜单时,一个典型需求是:当用户将鼠标悬停在“Categories”主菜单项上时,下方独立的 .categories-ul 子菜单应平滑展开。但问题在于——该子菜单并非嵌套在 <li class="categories"> 内部,而是与主菜单 <ul> 并列存在于 <nav> 中。因此,传统后代选择器(如 .categories:hover .categories-ul)完全失效,因为二者没有 DOM 包含关系。
此时,CSS 新增的 :has() 关系伪类成为理想解法。它允许我们基于某个元素是否匹配特定条件(例如其内部是否存在被悬停的 .categories),来为该元素的祖先或同级元素设置样式。
关键实现步骤如下:
- 初始隐藏子菜单:将 .categories-ul 的 display 设为 none;
- 利用 :has() 定位触发容器:选择 <nav class="pc-nav"> 作为作用域,检查其内部是否存在 :hover 状态的 .categories 元素;
- 条件激活子菜单:当条件满足时,将 .categories-ul 切换为 display: flex,并可叠加其他悬停样式(如背景色、过渡动画等)。
以下是精简优化后的核心代码:
立即学习“前端免费学习笔记(深入)”;
.pc-nav-ul {
width: 50%;
display: flex;
justify-content: space-between;
border-bottom-left-radius: 2rem;
border-bottom-right-radius: 2rem;
backdrop-filter: hue-rotate(100deg) blur(5px);
}
.pc-nav-li {
font-size: 1.5rem;
font-family: var(--text-font);
padding: 1rem;
transition: background-color 0.6s, letter-spacing 0.6s;
}
.categories-ul {
backdrop-filter: hue-rotate(140deg) blur(20px);
padding: 0.7rem;
border-radius: 1rem;
width: 45rem;
display: none; /* 初始隐藏 */
justify-content: space-evenly;
position: fixed;
top: 5rem;
z-index: 99999;
transition: all 0.6s ease;
}
.categories-li {
font-family: var(--text-font);
font-size: 1.2rem;
}
.pc-nav-li:hover:not(.categories-li) {
letter-spacing: 2px;
}
/* ✅ 核心逻辑:当 .pc-nav 内存在悬停的 .categories 时,显示同级 .categories-ul */
.pc-nav:has(.categories:hover) .categories-ul {
display: flex;
background-color: rgba(255, 0, 0, 0.1); /* 推荐使用半透明色提升可读性 */
}<nav class="pc-nav">
<ul class="pc-nav-ul">
<li class="pc-nav-li"><a href="#">home</a></li>
<li class="pc-nav-li categories"><a href="#">categories</a></li>
<li class="pc-nav-li"><a href="#">setting</a></li>
<li class="pc-nav-li"><a href="#">login/sign up</a></li>
</ul>
<!-- 子菜单与 pc-nav-ul 同级,位于 nav 内 -->
<ul class="categories-ul">
<li class="categories-li"><a href="#">entertainment</a></li>
<li class="categories-li"><a href="#">literature</a></li>
<li class="categories-li"><a href="#">science</a></li>
<li class="categories-li"><a href="#">programming</a></li>
</ul>
</nav>⚠️ 重要注意事项:
- :has() 目前在 Chrome 105+、Edge 105+、Safari 15.4+ 中已稳定支持;Firefox 已实现但默认关闭(需手动启用 layout.css.has-selector.enabled),生产环境务必添加渐进增强策略(例如配合 JavaScript fallback 或降级为点击展开);
- 避免在 :has() 中使用过于复杂的选择器,以免影响性能;
- 若需支持旧浏览器,推荐将子菜单结构改为语义化嵌套(即 <li class="categories"> 内直接包含 <ul class="categories-ul">),再用 .categories:hover .categories-ul 控制显示。
通过 :has(),我们摆脱了对 JavaScript 的强依赖,在纯 CSS 层面实现了跨层级交互逻辑——这是现代前端开发中声明式思维的有力体现。


















