You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript初始化后Offcanvas关闭按钮失效问题排查

问题解决:Bootstrap Offcanvas 大屏显示后小屏关闭按钮失效

问题场景

将Offcanvas作为页面布局组件,需求如下:

  • 默认不显示,大屏设备(≥992px)始终显示
  • 小屏设备提供关闭按钮,下方菜单面板有打开Offcanvas的按钮
  • 页面加载和窗口resize时,大屏自动显示Offcanvas;缩小屏幕后,Offcanvas保持可见直到手动关闭

出现的问题:仅当Offcanvas未通过JavaScript初始化时,关闭按钮正常工作。一旦通过JS在大屏触发显示后,缩小屏幕时关闭按钮失效。

问题原因

  1. 重复初始化Offcanvas实例:每次调用adjust_layout都创建新的bootstrap.Offcanvas实例,重复初始化会覆盖原有的事件监听器,导致data-bs-dismiss等内置交互逻辑失效。
  2. 未跟踪用户手动操作状态:resize时没有区分是系统强制显示还是用户手动控制的状态,导致交互逻辑混乱。

解决方案

  1. 全局单例实例:只初始化一次Offcanvas实例,避免重复创建覆盖事件
  2. 状态跟踪:记录用户是否手动关闭过Offcanvas,resize时根据屏幕尺寸和用户状态决定显示/隐藏
  3. 防抖优化: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 21:44:57