Bootstrap 5 原生不支持响应式多级折叠菜单,必须手动为每级 .collapse 创建独立 Collapse 实例,同步 aria-expanded、管理焦点与 ESC 关闭,并在移动端 resize 时主动重置非根级展开状态。
bootstrap 5 原生不支持响应式多级折叠菜单——data-bs-toggle="collapse" 套娃写法会失效,所有子菜单共用同一收起逻辑,点开一级就全展开,根本没法逐级控制。
为什么直接套 data-bs-parent 不行
Bootstrap 的 data-bs-parent 只管理「同级兄弟折叠项」的互斥行为,对嵌套结构无感知。你给二级菜单加 data-bs-parent="#first-level",它不会自动监听父级是否展开;父级收起时,二级也不会跟着收,反而可能悬空显示。更麻烦的是,移动端断点(如 sm)下,所有 .collapse 默认都该收起,但原生机制做不到按层级自动重置状态。
必须手动实例化每个 .collapse
放弃纯 HTML 触发,改用 JavaScript 显式创建每个折叠实例,并绑定独立生命周期。这是唯一能稳定控制多级展开/收起的路径:
- 每级菜单容器(
<div class="collapse">)必须有唯一 <code>id或可定位的class - 用
new bootstrap.Collapse(element, { toggle: false })初始化,避免自动触发 - 监听父级按钮点击事件,调用对应子级的
show()或hide()方法 - 在
resize事件中检测屏幕宽度,sm断点以下主动调用所有子级hide() - 不要只靠 CSS
@media (max-width: 575.98px)隐藏元素——视觉隐藏不等于状态重置,aria-expanded仍为true,屏幕阅读器会误读 - 每次窗口 resize 后,遍历所有
.collapse.show,排除根级后全部调用instance.hide() - 根级按钮(如一级菜单项)需保留
aria-expanded同步逻辑,且默认在移动端为false - 每次调用
show()/hide()后,手动更新对应按钮的aria-expanded="true/false" - 子菜单展开后,焦点应自动移入第一个可聚焦元素(如
button或a),否则 Tab 键会跳过整个子树 - ESC 键需监听并关闭当前展开的最深层级,而不是直接关掉所有
- 用
document.querySelectorAll('.collapse').forEach(...)获取实例时,注意 DOM 加载顺序,确保元素已挂载
移动端必须强制收起所有子级
桌面端允许展开当前路径,但移动端(window.innerWidth )必须清空所有非根级折叠状态,否则侧边栏会撑出视口、滚动异常、触控区域错位:
别漏掉 aria-expanded 和键盘焦点
仅控制显隐不够,无障碍和键盘导航依赖属性同步:
真正跑通的关键不在结构多复杂,而在于每一级 .collapse 是否拥有独立实例、独立状态、独立响应逻辑——任何试图“偷懒”复用同一个 toggle 或依赖父级 data-bs-target 的写法,都会在移动端或键盘操作时暴露问题。


















