动态导航菜单的核心是实时响应用户操作或数据变化,而非仅添加动画;纯CSS方案(如checkbox+~)轻量兼容,但JS更适合多级或状态记忆场景,且必须同步aria属性与视觉状态。

动态导航菜单不是靠“加动画”就叫动态,而是指菜单结构、状态或内容能随用户操作或数据变化实时响应。纯 CSS 实现的 hover 展开、checkbox 控制显隐,或者 JS 驱动的数据渲染,都属于动态范畴——但它们适用场景和限制完全不同。
用 checkbox + :checked + ~ 实现无 JS 下拉菜单
这是最轻量、兼容性好、且无需运行时脚本的动态方案,适合静态站点或对 JS 加载敏感的场景。
-
<input type="checkbox" id="menu-toggle">必须放在<ul class="dropdown">前面,且中间不能插入其他块级元素,否则~选择器失效 - 菜单项需用
<label for="menu-toggle">触发,不能只靠点击<li>—— 否则键盘 Tab 无法聚焦、屏幕阅读器不识别操作意图 - 展开时别用
display: block,改用opacity和max-height配合transition,否则动画会卡顿或直接跳变 - iOS Safari 15.4 以下版本存在 focus 失效问题,若需支持,得 fallback 到 JS 监听
click并切换aria-expanded
用 classList.toggle() 控制多级菜单展开状态
当菜单层级超过两级、或需要记忆上次展开项(比如手风琴效果),JS 是更可控的选择。
- 不要直接操作
style.display,统一用类名控制,例如menu-item--open,便于 CSS 维护和动画衔接 - 必须同步更新
aria-expanded和aria-hidden,否则 VoiceOver 或 NVDA 会读错状态 - 避免在循环中频繁调用
getBoundingClientRect()或触发重排,尤其是移动端;展开前先设height: 0,再用offsetHeight获取目标高度,最后设height: auto→height: ${targetHeight}px - 如果菜单项来自 API,注意首次渲染后才绑定事件,否则
document.querySelectorAll('.dropdown-toggle')可能返回空 NodeList
用 nav + ul + li 构建语义化骨架,而非 menu 标签
<menu> 在现代浏览器中行为不一致,Chrome 当作 <ul> 渲染,Firefox 可能忽略其语义,Lighthouse 会报 “Navigation landmark not found”。
立即学习“前端免费学习笔记(深入)”;
- 始终用
<nav aria-label="主导航">包裹整个菜单,aria-label不可省略,否则屏幕阅读器只读“列表”,不读“导航” - 每个一级菜单项是
<li><a href="/home">首页</a></li>,二级菜单嵌套在<ul class="submenu">内,结构必须合法嵌套 - 不要为了“扁平化”把所有链接塞进一个
<div>,这会让键盘 Tab 顺序混乱、焦点管理失效、SEO 权重分散 - 如果用 Vue/React 动态生成,输出 HTML 仍应保持
<nav><ul><li><a>结构,框架层只是渲染器,语义责任在最终 DOM
真正容易被忽略的不是动画怎么写,而是状态同步——比如 JS 展开菜单后,CSS 没配 transform 或 opacity 过渡,结果只有文字颜色变,下拉框却“啪”一下弹出来;又或者 checkbox 方案里忘了加 aria-checked,导致残障用户根本不知道菜单已打开。



















