纯 CSS 响应式多级下拉菜单的关键在于正确使用 @media (hover: hover) 和 (pointer: fine) 判断交互能力,而非屏幕尺寸;每级菜单项必须设 position: relative,子菜单需 left: 100% 定位;须同时支持 :hover 和 :focus-within,移动端用 data-open 控制 opacity/visibility 过渡,确保可访问性与视觉连续性统一。

纯 CSS 实现响应式多级下拉菜单,关键不在“快”,而在“不踩坑”——多数失败源于用 @media (max-width: 768px) 切换交互逻辑,或忽略 :focus-within 和 position: relative 的强制配对。
为什么 @media (hover: none) 比 @media (max-width: 768px) 更可靠
设备是否支持 hover,和屏幕宽窄无关。Galaxy Z Fold 横屏时宽度超 1000px,但 hover 不可用;1366×768 的 Windows 笔记本却完全支持精细指针。用尺寸断点会误判交互能力。
-
@media (hover: hover) and (pointer: fine):启用.has-submenu:hover > .submenu -
@media (hover: none), (pointer: coarse):禁用所有:hover显隐,只保留:focus-within和 JS 控制的[data-open="true"] - 别在移动断点里写
.submenu { display: block; }——这会让子菜单默认展开,失去控制权
position: relative 必须每级都加,不是只加一级
只给一级 li 加 position: relative,二级菜单会相对视口定位(而不是父项),导致错位;没加,键盘用户 Tab 进入后菜单根本不显示。
- 每一级可展开项(
)都要设position: relative - 对应子菜单(
)必须是其直接子元素 - 二级起的子菜单需额外设
left: 100%(而非统一top: 0; left: 0)
鼠标移向子菜单时闪退?这不是 bug,是视觉连续性没做
最常见问题:鼠标从父项移到子菜单途中,因两者间有空隙,:hover 状态丢失,菜单瞬间收起。
- 用
padding或margin把父容器向下/向右延展,覆盖过渡区域 - 避免单纯靠
top: 100%悬空子菜单;改用transform: translateY(100%)+transition更稳 - 务必同时写
.has-submenu:hover > .submenu和.has-submenu:focus-within > .submenu,缺一不可
移动端点击展开必须用 data-open,别碰 display: none
用 display: none 切换 DOM 可见性,会导致屏幕阅读器跳过整个子菜单结构,且无法做 opacity/height 过渡动画。
- 子菜单初始状态应为
opacity: 0; visibility: hidden; - JS 只负责切换
[data-open="true"],CSS 控制显隐与过渡:[data-open="true"] .submenu { opacity: 1; visibility: visible; } - 动画建议用
opacity+visibility,而非height或max-height(后者难精确控制)
真正难的不是写多少行 CSS,而是让 hover、focus、touch、键盘导航、屏幕阅读器全部走同一套语义路径——class="has-submenu" 和 class="submenu" 的命名、嵌套层级、定位锚点,一个都不能松动。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











