Bootstrap 不支持纯 CSS 的响应式二级下拉菜单 hover 触发,因移动端无 hover 状态且 JS 阻断 CSS 逻辑;推荐使用官方嵌套 dropdown 结构,通过 data-bs-toggle="dropdown" 和 JS 实现全平台一致的点击展开。

Bootstrap 本身不支持纯 CSS 实现的响应式二级联动下拉菜单悬浮触发(hover)——尤其在移动端会失效,且官方明确弃用 hover 作为下拉主交互方式。 这是设计决策,不是你代码写错了。
为什么 dropdown-menu 悬浮在移动端根本不起作用
Bootstrap 5+ 移动端默认禁用 :hover 触发逻辑,因为触摸设备无 hover 状态;dropdown 组件依赖 data-bs-toggle="dropdown" + JS 监听 click 事件,CSS 的 :hover 规则会被 JS 阻断或忽略。即使你强行加 :hover .dropdown-menu { display: block; },在 iOS Safari 或 Android Chrome 上大概率出现菜单闪退、点击穿透、焦点丢失等问题。
- 真实现象:鼠标悬停 PC 端能展开,但手指轻点一级菜单后二级菜单不出现,或出现后立刻消失
- 根本原因:Bootstrap 的
DropdownJS 会监听hide.bs.dropdown并主动隐藏,而 hover 无法同步控制 JS 状态 - 兼容性影响:所有启用了
data-bs-toggle的父级元素都会接管交互逻辑,CSS hover 成为“无效覆盖”
如何用原生 Bootstrap 实现「点击展开」的二级联动(推荐方案)
放弃 hover,改用官方支持的嵌套 dropdown 结构,靠 JS 控制二级菜单显隐,确保全平台一致行为。
- 一级菜单必须带
data-bs-toggle="dropdown",且.dropdown-menu内部可嵌套另一个.dropdown - 二级菜单容器需加
data-bs-popper="static"避免 Popper.js 错位(尤其在 navbar 中) - 必须为二级触发项添加
data-bs-toggle="dropdown",否则 JS 不接管 - 示例结构关键片段:
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">产品</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Web 端</a></li>
<li class="dropdown">
<a class="dropdown-item dropdown-toggle" href="#" data-bs-toggle="dropdown">移动端</a>
<ul class="dropdown-menu" data-bs-popper="static">
<li><a class="dropdown-item" href="#">iOS</a></li>
<li><a class="dropdown-item" href="#">Android</a></li>
</ul>
</li>
</ul>
</li>
注意:dropdown-toggle 类只是样式标识,真正起作用的是 data-bs-toggle="dropdown" 属性和对应 JS 初始化。
如果非要保留 hover(仅限桌面端),必须绕过 Bootstrap JS
禁用 Bootstrap 的 Dropdown 插件,改用纯 CSS 实现,但要手动处理移动端降级逻辑。
- 移除所有
data-bs-toggle="dropdown"和相关 JS 初始化(如new bootstrap.Dropdown()) - 用 CSS 控制显示:
.dropdown:hover .dropdown-menu {
display: block;
}
.dropdown-menu {
display: none;
}
- 加媒体查询强制移动端回退为 click:
@media (max-width: 991.98px) {
.dropdown-menu {
display: none !important;
}
.dropdown.show .dropdown-menu {
display: block !important;
}
}
- 再用少量 JS 补齐移动端 toggle(仅监听 click,不依赖 Bootstrap):
document.querySelectorAll('.dropdown').forEach(el => {
el.addEventListener('click', e => {
if (window.innerWidth < 992) {
e.preventDefault();
el.classList.toggle('show');
}
});
});
这个方案代价高:失去 Popper 定位、滚动跟随、自动关闭等特性,且需自己维护 focus/keyboard 支持。
容易被忽略的关键细节
二级菜单的 .dropdown-menu 默认 z-index 是 1000,但如果父容器有 overflow: hidden(比如 .navbar 在收缩状态),菜单会被裁剪——必须给父级加 overflow: visible 或提升 z-index;另外,data-bs-popper="static" 不是可选配置,而是防止二级菜单被错误定位到视口边缘的必要设置;最后,dropdown-item 里不能再套 dropdown,必须套在 li 元素里,否则语义错乱且 JS 不识别。


















