手风琴下拉菜单必须用JavaScript控制互斥展开,CSS仅负责过渡;需用data-target关联按钮与submenu,动态设置max-height实现平滑动画,并添加ARIA属性和键盘支持以满足无障碍要求。

手风琴下拉菜单本质是单选折叠,不是纯CSS能可靠实现的
纯用 :hover 或 :focus-within 做横向导航的手风琴效果,会在移动端失效、键盘导航不友好、多个子菜单同时展开无法强制互斥。真正可用的方案必须依赖 JavaScript 控制展开状态,CSS 只负责过渡和视觉收放。
关键约束:横向导航栏里每个 <li> 是一个主项,点击时只展开它自己的 <ul class="submenu"></ul>,同时收起其他已展开的同类菜单。
用 querySelectorAll + dataset 管理开关状态最轻量
不需要框架,几行 JS 就能搞定。核心是给每个触发元素加 data-target,指向对应下拉容器;再用一个变量记住当前打开的是哪个,避免遍历全部 DOM。
- HTML 结构中,主菜单项用
<button type="button"></button>替代<a></a>,防止默认跳转干扰 - 下拉容器统一加
class="submenu",初始设style="max-height: 0; overflow: hidden;" - JS 里用
element.dataset.target关联按钮和下拉区,比如data-target="nav-products"对应id="nav-products" - 每次点击先遍历已开的
.submenu,设max-height = 0,等 transition 结束后再打开目标项(或直接用height: auto配合getBoundingClientRect()动态计算)
max-height 过渡比 height 更安全但有隐含限制
height: auto 无法被 CSS transition 直接驱动,所以普遍用 max-height 模拟。但这个 trick 有硬伤:
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
立即学习“前端免费学习笔记(深入)”;
- 如果下拉内容高度超过预设的
max-height(比如写死max-height: 500px),末尾会被截断 - 如果设得过大(如
max-height: 10000px),展开动画会变慢——浏览器仍要完成整个过渡过程 - 更稳的做法是:点击后先临时移除
max-height,读取scrollHeight,再设回带过渡的max-height值,收起时再设为 0
示例关键片段:
const submenu = document.getElementById(targetId); submenu.style.maxHeight = submenu.scrollHeight + 'px';
横向导航需额外处理焦点与键盘操作
仅靠鼠标点击不够。用户按 Tab 进入菜单后,应支持 Enter/Space 展开、Escape 收起、ArrowDown 进入子菜单首项——否则不符合 WCAG 2.1。
- 主菜单项必须有
role="menuitem",外层<ul role="menu"></ul> - 下拉容器加
role="menu",子链接加role="menuitem" - 用
aria-expanded="true/false"同步更新按钮状态,屏幕阅读器才感知得到 - 注意:
ArrowUp/ArrowDown在横向菜单中通常用于在主项间切换,不是展开逻辑
容易被忽略的一点:移动端 touchstart 触发后,若没阻止默认行为,快速点击可能触发两次,导致闪退。加 event.preventDefault() 在按钮上即可。


















