JavaScript初始化后Offcanvas关闭按钮失效问题排查
问题解决:Bootstrap Offcanvas 大屏显示后小屏关闭按钮失效
问题场景
将Offcanvas作为页面布局组件,需求如下:
- 默认不显示,大屏设备(≥992px)始终显示
- 小屏设备提供关闭按钮,下方菜单面板有打开Offcanvas的按钮
- 页面加载和窗口resize时,大屏自动显示Offcanvas;缩小屏幕后,Offcanvas保持可见直到手动关闭
出现的问题:仅当Offcanvas未通过JavaScript初始化时,关闭按钮正常工作。一旦通过JS在大屏触发显示后,缩小屏幕时关闭按钮失效。
问题原因
- 重复初始化Offcanvas实例:每次调用
adjust_layout都创建新的bootstrap.Offcanvas实例,重复初始化会覆盖原有的事件监听器,导致data-bs-dismiss等内置交互逻辑失效。 - 未跟踪用户手动操作状态:resize时没有区分是系统强制显示还是用户手动控制的状态,导致交互逻辑混乱。
解决方案
- 全局单例实例:只初始化一次Offcanvas实例,避免重复创建覆盖事件
- 状态跟踪:记录用户是否手动关闭过Offcanvas,resize时根据屏幕尺寸和用户状态决定显示/隐藏
- 防抖优化:resize事件添加防抖,避免频繁触发逻辑
修改后的代码
HTML(无需修改)
<!-- Button to show offcanvas (positioned underneath it) --> <div> <button class="btn" type="button" id="off-canvas-body-toggle" data-bs-toggle="offcanvas" data-bs-target="#floating-menu"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#422543" class="bi bi-arrow-bar-right" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M6 8a.5.5 0 0 0 .5.5h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L12.293 7.5H6.5A.5.5 0 0 0 6 8Zm-2.5 7a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5Z" /> </svg> </button> </div> <!-- Offcanvas --> <div class="offcanvas offcanvas-start" data-bs-scroll="true" data-bs-backdrop="false" tabindex="-1" id="floating-menu"> <!-- Bootstrap classes d-xl-none d-lg-none hides the div with dismiss button on large screen --> <!-- But it becomes visible when the screen is downsized --> <div class="offcanvas-header d-xl-none d-lg-none"> <!-- Dismiss button that does not work corectly --> <button type="button" class="btn" id="off-canvas-body-dismiss" data-bs-dismiss="offcanvas"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#422543" class="bi bi-arrow-bar-left" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5ZM10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5Z" /></svg> </button> </div> <div class="offcanvas-body"> <!-- This contains an accordion and works fine --> </div> </div>
JavaScript
// 全局变量保存Offcanvas实例和用户手动关闭状态 let bsOffcanvas; let userClosed = false; // 防抖函数,避免resize频繁触发 function debounce(func, delay = 200) { let timeoutId; return function() { clearTimeout(timeoutId); timeoutId = setTimeout(func, delay); }; } function adjust_layout() { const width = window.innerWidth; const isLargeScreen = width >= 992; if (isLargeScreen) { // 大屏强制显示,重置用户关闭状态 userClosed = false; bsOffcanvas.show(); } else { // 小屏状态下,只有用户手动关闭过才隐藏,否则保持当前显示状态 if (userClosed) { bsOffcanvas.hide(); } } } document.addEventListener("DOMContentLoaded", function () { // 只初始化一次Offcanvas实例 const menu = document.getElementById("floating-menu"); bsOffcanvas = new bootstrap.Offcanvas(menu); // 监听Offcanvas关闭事件,记录用户手动关闭状态 menu.addEventListener('hidden.bs.offcanvas', () => { userClosed = true; }); // 监听Offcanvas打开事件,重置用户关闭状态 menu.addEventListener('shown.bs.offcanvas', () => { userClosed = false; }); // 初始化布局 adjust_layout(); // 绑定防抖后的resize事件 window.addEventListener("resize", debounce(adjust_layout)); });
关键说明
- 单例实例:仅在DOM加载完成时创建一次
bootstrap.Offcanvas实例,确保事件监听器不被重复覆盖 - 状态跟踪:通过
userClosed变量记录用户是否手动关闭过Offcanvas,小屏时尊重用户操作 - 防抖处理:resize事件添加防抖,减少不必要的逻辑触发,提升性能
内容的提问来源于stack exchange,提问作者VikSil
相关产品推荐
相关产品推荐

