Bootstrap 5 默认不支持多级下拉菜单,需自行通过CSS hover实现二级菜单或用JavaScript补全三级交互,禁用第三方插件以避免兼容性与无障碍问题。
Bootstrap 5 默认不支持多级下拉菜单
bootstrap 5 官方移除了对二级及以上下拉菜单的原生支持,dropdown-menu 默认只响应一级 dropdown-item 的 hover/focus,嵌套的 dropdown-menu 不会自动展开。这不是你代码写错了,是框架故意没做。
常见错误现象:Dropdown menu appears but submenus don’t open on hover/click、Submenu items are visible but unclickable、JavaScript throws “Cannot read property 'addEventListener' of null”(因 DOM 未就绪或选择器错)。
使用场景:后台管理导航、文档侧边栏、复杂产品分类栏——只要需要 >1 级折叠结构,就得自己补逻辑。
用纯 CSS 实现二级菜单(无 JS,兼容性好)
核心思路:靠 :hover 触发子菜单显示,依赖父级 dropdown 容器保持定位上下文。适合鼠标优先、不强依赖移动端触控的场景。
关键点:
- 必须给父级
dropdown加position: relative(Bootstrap 5 默认有,但自定义结构里容易漏) - 子
dropdown-menu要设position: absolute; top: 0; left: 100%,并隐藏溢出 - 父
dropdown-item需设position: relative,否则子菜单定位会错乱 - 移动端需额外处理:CSS
@media (max-width: 991.98px)下禁用:hover,改用 JS click 切换
示例片段:
<div class="dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">Products</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Category A</a></li>
<li class="dropdown">
<a class="dropdown-item dropdown-toggle" href="#">Category B</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Subitem 1</a></li>
<li><a class="dropdown-item" href="#">Subitem 2</a></li>
</ul>
</li>
</ul>
</div>
对应 CSS(必须加):
.dropdown-menu .dropdown-menu {
top: 0;
left: 100%;
margin-top: -1px;
}
.dropdown-menu .dropdown:hover .dropdown-menu {
display: block;
}
@media (max-width: 991.98px) {
.dropdown-menu .dropdown:hover .dropdown-menu {
display: none;
}
}
用 JavaScript 补全三级菜单交互(推荐用于生产环境)
纯 CSS 在触屏设备上不可靠,且无法处理键盘导航(如 Tab 进入子菜单)。真实项目建议用轻量 JS 控制 show/hide 状态,复用 Bootstrap 5 的 Dropdown 实例 API。
要点:
- 不能直接对子
.dropdown-menu初始化bootstrap.Dropdown,它只认顶层data-bs-toggle="dropdown" - 正确做法:监听父级
dropdown-item的mouseenter/click,手动调用子菜单的show()方法 - 必须清理旧状态:每次 show 前 hide 所有兄弟子菜单,避免多个同时展开
- 键盘支持:监听
ArrowRight进入子菜单,ArrowLeft返回上级,Escape关闭全部 - 性能注意:别在
document.addEventListener('click')里做深度遍历,用事件委托到 navbar 容器
最小可用逻辑(不依赖 jQuery):
document.querySelectorAll('.dropdown-submenu a[data-bs-toggle="dropdown"]').forEach(el => {
el.addEventListener('click', e => {
e.preventDefault();
const parent = el.closest('.dropdown-submenu');
const submenu = parent.querySelector('.dropdown-menu');
if (submenu) {
// 先关掉同级其他子菜单
parent.parentElement.querySelectorAll('.dropdown-menu').forEach(m => {
if (m !== submenu) m.classList.remove('show');
});
submenu.classList.toggle('show');
}
});
});
为什么不要用 bootstrap-submenu 或其他第三方插件
很多老教程推荐 bootstrap-submenu 或 smartmenus,但它们存在明显问题:
- 绝大多数未适配 Bootstrap 5 的 DOM 结构变更(比如移除了
.dropdown-menu-right类) - 强行 patch
Dropdown构造函数,导致和官方更新冲突,升级 Bootstrap 时大概率报错 - 添加大量冗余 CSS 和事件监听,影响首屏渲染和滚动性能
- 不支持 WAI-ARIA 标准属性(如
aria-expanded),无障碍测试失败
真正省事的做法:用上面 JS 片段 + 3 行 CSS,控制权完全在自己手里,后续加动画、加延迟、加点击穿透逻辑都方便改。
最容易被忽略的是键盘焦点管理——用户按 Tab 进入子菜单后,焦点不会自动跳进第一个子项,得手动 submenu.querySelector('a').focus(),否则 WCAG 2.1 AA 就不达标。


















