纯CSS实现二级导航菜单平滑展开的关键是避免display:none和height:auto,改用opacity+transform组合:初始设opacity:0、visibility:hidden、transform:scaleY(0.8);悬停时切为opacity:1、visibility:visible、transform:scaleY(1),并确保transition声明在默认状态且父级有position:relative。

纯 CSS 实现二级导航菜单的平滑展开,关键不是加不加 :hover,而是怎么绕过 display: none 和 height: auto 这两个“动画杀手”。直接写 transition: height 0.3s 会失效,display 根本不能过渡——得用可动画属性替代。
为什么 display: none + transition 不生效
CSS 的 display 属性是离散值(none/block),浏览器无法插值计算中间状态,所以加了 transition 也白搭。常见错误是这么写:
.submenu { display: none; }
.dropdown:hover .submenu { display: block; transition: all 0.3s; }
结果就是闪一下就出来,毫无过渡感。真正起作用的必须是连续可动画的属性,比如 opacity、transform、max-height(有限制)。
opacity + transform 是最稳的组合
这个方案兼容性好(IE10+)、性能高、无重排,且能自然控制显隐节奏:
- 父级
.dropdown必须设position: relative,否则子菜单的position: absolute会相对于 body 定位,飘走 - 子菜单初始状态:用
opacity: 0+visibility: hidden+transform: scaleY(0.8) translateZ(0)(translateZ(0)强制 GPU 加速) - 悬停时只改
opacity: 1、visibility: visible、transform: scaleY(1) -
transition必须写在子菜单默认状态上,不是写在:hover规则里:.submenu { transition: opacity 0.2s ease, transform 0.2s ease; }
别碰 height 过渡,除非你限制死高度
有人试 max-height: 0 → max-height: 300px,看似能动,但隐患多:
- 内容高度不确定时,硬写
300px容易截断或留白过多 -
max-height过渡在 Safari 旧版有渲染抖动 - 哪怕设成
max-height: 100vh,首次展开仍可能卡顿(浏览器要计算 layout) - 如果子菜单里有
margin或padding变化,它不会被包含在max-height动画里,视觉撕裂
真要用高度变化,不如配合 transform: scaleY() 控制缩放,更可控。
移动端和可访问性必须手动补救
:hover 在触屏设备上行为不可靠(iOS Safari 可能需要点击两次才触发,Android Chrome 常忽略),而且键盘用户、屏幕阅读器完全无法操作:
- 不要依赖纯
:hover做核心导航——至少提供focus-within回退:.dropdown:focus-within .submenu,让 Tab 键能打开 - 给
.dropdown加tabindex="0",确保可聚焦 - 触屏设备建议用 JS 补一层点击 toggle,CSS 只负责动画部分(
opacity/transform),不接管显隐逻辑 - 用
aria-expanded和aria-haspopup="menu"标注状态,否则屏幕阅读器读不出“这是下拉菜单”
最常被忽略的一点:很多人调好了动画,却忘了 z-index。如果子菜单被其他元素(比如 banner 图片、fixed header)盖住,再顺滑也没用——务必检查层级是否足够高。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











