用classList.toggle()切换菜单显隐最直接,避免全量赋值className;用max-height过渡实现滑动动画,禁用height:auto;移动端需处理焦点残留与连点节流,并添加ARIA属性保障可访问性。

点击按钮触发 classList.toggle() 控制菜单显隐
横向导航的展开折叠,本质是切换一个 CSS 类来控制 display 或 max-height 状态。用 classList.toggle() 最直接——它不依赖额外状态变量,点一下开、再点一下关,逻辑干净。
常见错误是写成 element.className = "show" 这种全量赋值,会清掉其他 class(比如响应式工具类或 JS 标记类),导致样式或行为异常。
- 按钮绑定
onclick或监听click事件,调用navMenu.classList.toggle("active") -
navMenu是包裹所有导航项的容器,比如<nav id="main-nav"><ul>...</ul></nav> - 避免对
<ul></ul>直接操作:它通常被嵌套在<nav></nav>或<header></header>里,CSS 动画/过渡需作用于外层容器才稳定
max-height 过渡比 display 更适合做动画
display: none 无法参与 CSS 过渡(transition),设了也没用;想让菜单滑动展开,必须用可动画属性,max-height 是最稳妥的选择。
别用 height: auto 配 transition——浏览器无法计算 auto 的终点值,动画会卡在 0 或突兀跳变。
立即学习“前端免费学习笔记(深入)”;
- 默认状态设
max-height: 0+overflow: hidden - 激活状态设
max-height: 500px(值要大于菜单最大可能高度,但不宜过大,否则动画过慢) - 加
transition: max-height 0.3s ease-in-out,注意单位必须是 px/em/rem,不能是none或auto
移动端点击穿透和焦点管理容易被忽略
在触摸设备上,如果菜单收起后按钮仍处于 focus 状态,再次点击可能不触发(尤其 Safari)。更隐蔽的问题是:展开时若用户快速连点两次,toggle() 会执行两次,导致状态错乱(比如刚展开又立刻收起)。
- 收起菜单后主动调用
button.blur(),清除焦点防止误触 - 加节流:用
setTimeout或disabled属性临时禁用按钮 300ms,例如btn.disabled = true; setTimeout(() => btn.disabled = false, 300); - 确保
@media查询中,横向导航只在桌面端显示(display: flex),移动端默认隐藏并启用折叠按钮
用 aria-expanded 和 aria-controls 保证可访问性
纯视觉切换对屏幕阅读器用户是黑盒。不加 ARIA 属性,他们点按钮后根本不知道菜单是否已展开,也无法快捷跳转到导航内容。
- 按钮上加
aria-expanded="false",切换时同步更新为"true"或"false" - 按钮加
aria-controls="nav-menu-id",指向菜单容器的id - 菜单容器加
role="navigation"和aria-labelledby(指向按钮 id),形成语义闭环



















