Offcanvas 必须挂载在<body>下,否则显示异常或无法关闭;data-bs-placement与类名不可混用;offcanvas-body需设max-height和overflow-y;遮罩失效多因id不匹配或DOM未就绪。

Offcanvas 必须挂载在 <body> 下,否则会显示异常或无法关闭
这是最常被忽略的硬性约束:Bootstrap 5 的 offcanvas 组件依赖 document.body 做事件委托和遮罩层插入。如果把它写在某个 <div class="container"> 或 <main> 里,点击遮罩不关闭、ESC 失效、滚动错位等问题几乎必然出现。
- 错误写法:
<main><div class="offcanvas offcanvas-start">...</div></main> - 正确写法:把整个
<div class="offcanvas">直接放在<body>最顶层,和<header>、<main>并列 - Vue/React 中注意:不要让组件渲染进局部容器(如
ref挂载点),要用createPortal或Teleport把 offcanvas 节点传到document.body
data-bs-placement 和类名必须严格对应,且只选其一
官方文档说支持 data-bs-placement="end",但实际中它仅用于 JS 初始化时的动态创建场景;绝大多数 HTML 静态用法必须靠类名控制位置——而且二者不能混用,否则行为不可预测。
- ✅ 推荐静态写法:
<div class="offcanvas offcanvas-end" id="nav"> - ❌ 混用失效:
<div class="offcanvas offcanvas-start" data-bs-placement="end" id="nav">—— 类名优先,data-bs-placement被忽略 - ⚠️ 动态 JS 创建时才用:
new bootstrap.Offcanvas(element, { placement: 'end' }),此时不写类名 - 方向类只有四个:
offcanvas-start、offcanvas-end、offcanvas-top、offcanvas-bottom,没有left/right别名
侧边栏内容滚动失效?关键在 offcanvas-body 的 CSS 控制
默认 offcanvas-body 不设高度限制,内容超长时会撑开整个抽屉,导致底部内容不可见、滚动条不出现在抽屉内——这不是 bug,是设计如此:你需要显式约束它的可滚动区域。
- 必须设置:
max-height: calc(100vh - 80px);(减去 header 高度) - 必须加:
overflow-y: auto;,且确保父级offcanvas没有overflow: hidden - 移动端 Safari 注意:
100vh在地址栏收起/展开时会跳变,建议改用100dvh(iOS 15+ 支持,老版本 fallback 用 JS 动态设) - 别在
offcanvas-header或offcanvas根元素上设height,会破坏动画流
遮罩(backdrop)关不掉?先检查 id 和 DOM 就绪时机
点击灰色遮罩无反应,90% 是因为触发按钮的 data-bs-target 指向的 id 和 offcanvas 实际 id 不一致,或 JS 执行时 offcanvas 还没被解析进 DOM。
- 确认两端
id完全一致(含大小写、连字符):data-bs-target="#nav-menu"↔<div class="offcanvas" id="nav-menu"> - 确保 offcanvas HTML 在页面加载时已存在——不要用 AJAX 异步插入后再调
show(),除非手动bootstrap.Offcanvas.getOrCreateInstance() - 禁用遮罩用:
data-bs-backdrop="false",不是false字符串,而是布尔属性(不带值)或"false"字符串都行,但推荐无值写法更稳妥 - 若用了自定义
z-index(比如 fixed header 设了z-index: 1100),确保 offcanvas 的默认z-index: 1200仍高于它
真实项目里最容易卡住的,其实是 DOM 挂载位置和滚动样式这两处——它们不报错,但表现诡异,查起来费时间。写完记得用手机真机测一次滚动和关闭,模拟器有时会掩盖 100dvh 兼容问题。


















