纯CSS自适应多级下拉菜单的关键是依据交互能力而非屏幕宽度:用@media (hover: hover) and (pointer: fine)启用:hover显隐,@media (hover: none), (pointer: coarse)禁用:hover改用:focus-within或JS控制;父级必须设position: relative,子菜单初始display: none,避免float导致z-index失效及空隙引发闪退。

纯 CSS 实现自适应屏幕宽度的多级下拉菜单,关键不在写多少媒体查询,而在正确判断设备交互能力。用 @media (max-width: 768px) 切换菜单行为,大概率会让折叠屏横屏时失效、小屏笔记本失去 hover,最终在真实设备上“半瘫痪”。
用 @media (hover: none) 替代 @media (max-width: ...)
桌面端靠 :hover,移动端必须放弃它——但不能靠屏幕宽度来猜,得看浏览器是否支持 hover。
-
@media (hover: hover) and (pointer: fine)下启用.has-submenu:hover > .submenu显隐逻辑 -
@media (hover: none), (pointer: coarse)下彻底禁用所有:hover控制,只保留:focus-within和 JS 驱动的[data-open="true"] - 别写
@media (max-width: 767px) { .submenu { display: block; } }——这会让子菜单默认展开,失去控制权 - Galaxy Z Fold 横屏宽度超 1000px 但不支持 hover;1366×768 的 Windows 笔记本却完全支持精细指针——尺寸不是决定因素
position: relative 缺失是二级菜单“消失”的头号原因
子菜单定位错乱、飘到左上角、鼠标一移就闪退,90% 是因为父级没设 position: relative 或隐藏方式错误。
- 每个带子菜单的
<li class="has-sub">必须显式声明position: relative,否则.submenu会相对于定位 -
.submenu初始状态必须是display: none,不能用visibility: hidden或opacity: 0——前者让 hover 失效,后者仍占交互区域且无法触发 :hover - 如果一级导航用了
float: left,z-index 会失效;换成display: flex并确保父容器有position: relative - 鼠标移入闪退?检查
.has-sub和.submenu之间有没有像素级空隙——用top: 100%定位,别用margin-top或top: auto
移动端点击展开后错位或瞬间关闭?别依赖 :hover 模拟
iOS Safari 和安卓 WebView 对 :hover 的模拟极不可靠:第一次点击可能被当 hover 触发,第二次才算 click,造成状态覆盖。
立即学习“前端免费学习笔记(深入)”;
- 彻底弃用
:hover控制显隐,改用 JS 显式切换:el.classList.toggle("is-open")或操作dataset.open - 子菜单定位别依赖
offsetTop——软键盘弹出、视口缩放都会破坏计算;改用position: fixed+getBoundingClientRect()动态算位置 - 展开前检查底部空间:
window.innerHeight - rect.bottom 就向上展开(<code>bottom: 100%),避免被裁剪 - 嵌套三级以上时,点开第三级常顺手关掉第二级——给子菜单的 click handler 加
event.stopPropagation()
max-height 过渡比 height: 0 → auto 更可控
height: 0 → auto 无法 transition,浏览器根本不知道 auto 是多少;直接切 display 又没法加动画。
- 统一用
max-height过渡:.submenu { max-height: 0; overflow: hidden; transition: max-height 0.3s ease, opacity 0.2s; - 展开时设为足够大的值,比如
max-height: 500px(要大于所有可能内容高度) - 配合
opacity或visibility做视觉增强,但不要单独靠它们控制显隐逻辑 - 如果菜单项文字过长导致换行错位,给
.submenu加white-space: nowrap和min-width: max-content
最易被忽略的一点:所有 :focus-within 规则必须和 :hover 同时存在,且每级菜单都要加 tabindex="0"——否则键盘用户 Tab 进入后菜单根本不显示,无障碍直接失败。


















