[js] 窗口宽度自适应变化(自动显示 / 隐藏侧边栏)

**目标:**优化思源笔记的侧边栏钉住逻辑,使其根据窗口宽度动态调整:

  1. 窗口最大化时:自动钉住左右两侧边栏(若空间足够)。
  2. 窗口半屏(或较窄)时:自动取消钉住侧边栏,避免遮挡编辑区域。
  3. 动态适应
    • 当窗口宽度 超出编辑器宽度一定阈值 时,优先钉住 左侧边栏
    • 若宽度 进一步增加(足以容纳两个侧边栏),则 同时钉住右侧边栏
    • 若窗口 变窄,则 依次取消钉住(右侧优先,左侧次之)。

期望效果:

  • 窗口较宽时(如全屏)
    全屏状态
    (左右侧边栏均钉住)
  • 窗口较窄时(如半屏)
    半屏状态
    (侧边栏自动隐藏,悬停展开)

实现思路(供开发者参考):

  • 检测窗口宽度,并与 编辑器内容区域宽度 对比。
  • 设定 钉住阈值(如 窗口宽度 > 编辑器宽度 + 侧边栏宽度)。
  • 使用 CSS Media QueriesJS 动态计算 调整钉住状态。

这样可以在不同窗口尺寸下提供更合理的布局,避免侧边栏遮挡内容,同时充分利用大屏空间。

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    28446 引用 • 119783 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    285 引用 • 1986 回帖
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    11155 引用 • 50664 回帖 • 52 关注
1 操作
Adaxi 在 2025-07-28 20:27:58 更新了该帖

相关帖子

被采纳的回答
  • wilsons 4 2 赞同

    其实我觉得不用这么麻烦,即无需这么多判断,只需判断窗口宽度即可。

    根据你的描述,大概有 3 种状态,1 两侧固定 2 仅左侧固定 3 两侧都不固定(隐藏)

    那么只需要判断 1 和 2 即可,且仅需判断窗口宽度即可,临界值根据自己需要修改。

    比如以下代码,经测试我觉得可以满足需求。

    // 窗口宽度自适应变化(自动显示/隐藏侧边栏)
    window.addEventListener('resize', () => {
        // 两侧固定阈值,当窗口大于这个值时两侧固定(可根据自己需要修改)
        const bothFixedBreakpoint = 1100;
      
        // 左侧固定阈值,当窗口大于这个值时左侧固定(可根据自己需要修改)
        const leftSideFixedBreakpoint = 800;
      
        if(!!document.getElementById("sidebar")) return; // 手机版跳过
        const leftPin = document.querySelector('#dockLeft .dock__item--pin');
        const rightPin = document.querySelector('#dockRight .dock__item--pin');
        const isPin = (pin) => !!pin.querySelector('svg use[*|href="#iconUnpin"]');
        const hideDock = (dock) => {
            let sign = '', dockEl;
            if(dock === 'left') {
                sign = '-';
                dockEl = document.querySelector('#layouts .layout__dockl.layout--float');
            } else {
                dockEl = document.querySelector('#layouts .layout__dockr.layout--float');
            }
            if(dockEl && !dockEl.style.transform) {
                // https://github.com/siyuan-note/siyuan/blob/5f0f4e3ba6aabd5af26f9879695e5b9b796b5fb9/app/src/layout/dock/index.ts#L478
                dockEl.style.transform = `translateX(${sign}${dockEl.clientWidth + 8}px)`;
                dockEl.style.opacity = "0";
            }
        }
        if(window.innerWidth > bothFixedBreakpoint) {
            // 两侧固定
            if(!isPin(leftPin)) leftPin.click();
            if(!isPin(rightPin)) rightPin.click();
        } else if(window.innerWidth > leftSideFixedBreakpoint) {
            // 左侧固定
            if(!isPin(leftPin)) leftPin.click();
            if(isPin(rightPin)) {rightPin.click();hideDock('right');}
        } else {
            // 两侧都不固定(隐藏)
            if(isPin(leftPin)) {leftPin.click();hideDock('left')}
            if(isPin(rightPin)) {rightPin.click();hideDock('right');}
        }
    });
    

    r159.gif


    版本 2,可能这个版本更符合你的需求

    // 窗口宽度自适应变化(自动显示/隐藏侧边栏)
    window.addEventListener('resize', () => {
        // 编辑器最小宽度,当窗口宽度大于编辑器最小宽度+两侧宽度时全固定,当仅容纳一个侧栏时左侧固定,否则全隐藏
        const minEditorWidth = 600;
    
        if(!!document.getElementById("sidebar")) return; // 手机版跳过
        const leftPin = document.querySelector('#dockLeft .dock__item--pin');
        const rightPin = document.querySelector('#dockRight .dock__item--pin');
        const isPin = (pin) => !!pin.querySelector('svg use[*|href="#iconUnpin"]');
        const hideDock = (dock) => {
            let sign = '', dockEl;
            if(dock === 'left') {
                sign = '-';
                dockEl = document.querySelector('#layouts .layout__dockl.layout--float');
            } else {
                dockEl = document.querySelector('#layouts .layout__dockr.layout--float');
            }
            if(dockEl && !dockEl.style.transform) {
                // https://github.com/siyuan-note/siyuan/blob/5f0f4e3ba6aabd5af26f9879695e5b9b796b5fb9/app/src/layout/dock/index.ts#L478
                dockEl.style.transform = `translateX(${sign}${dockEl.clientWidth + 8}px)`;
                dockEl.style.opacity = "0";
            }
        }
        const leftSideWdith = document.querySelector('#layouts .layout__dockl')?.offsetWidth || 0;
        const rightSideWidth = document.querySelector('#layouts .layout__dockr')?.offsetWidth || 0;
        if((window.innerWidth -  minEditorWidth) > (leftSideWdith + rightSideWidth)) {
            // 两侧固定
            if(!isPin(leftPin)) leftPin.click();
            if(!isPin(rightPin)) rightPin.click();
        } else if((window.innerWidth -  minEditorWidth) > leftSideWdith) {
            // 左侧固定
            if(!isPin(leftPin)) leftPin.click();
            if(isPin(rightPin)) {rightPin.click();hideDock('right');}
        } else {
            // 两侧都不固定(隐藏)
            if(isPin(leftPin)) {leftPin.click();hideDock('left')}
            if(isPin(rightPin)) {rightPin.click();hideDock('right');}
        }
    });
    

    版本 3,兼容性更好,兼容直接拖动侧边栏的情况

    // 窗口宽度自适应变化(自动显示/隐藏侧边栏)
    setTimeout(() => {
        // 编辑器最小宽度,当窗口宽度大于编辑器最小宽度+两侧宽度时全固定,当仅容纳一个侧栏时左侧固定,否则全隐藏
        const minEditorWidth = 600;
        
        const layoutCenter = document.querySelector('.layout__center');
        if(!layoutCenter) return;
        new ResizeObserver((entries) => {
            const leftPin = document.querySelector('#dockLeft .dock__item--pin');
            const rightPin = document.querySelector('#dockRight .dock__item--pin');
            const isPin = (pin) => !!pin.querySelector('svg use[*|href="#iconUnpin"]');
            const hideDock = (dock) => {
                let sign = '', dockEl;
                if(dock === 'left') {
                    sign = '-';
                    dockEl = document.querySelector('#layouts .layout__dockl.layout--float');
                } else {
                    dockEl = document.querySelector('#layouts .layout__dockr.layout--float');
                }
                if(dockEl && !dockEl.style.transform) {
                    // https://github.com/siyuan-note/siyuan/blob/5f0f4e3ba6aabd5af26f9879695e5b9b796b5fb9/app/src/layout/dock/index.ts#L478
                    dockEl.style.transform = `translateX(${sign}${dockEl.clientWidth + 8}px)`;
                    dockEl.style.opacity = "0";
                }
            }
            const leftSideWdith = document.querySelector('#layouts .layout__dockl')?.offsetWidth || 0;
            const rightSideWidth = document.querySelector('#layouts .layout__dockr')?.offsetWidth || 0;
            if((window.innerWidth -  minEditorWidth) > (leftSideWdith + rightSideWidth)) {
                // 两侧固定
                if(!isPin(leftPin)) leftPin.click();
                if(!isPin(rightPin)) rightPin.click();
            } else if((window.innerWidth -  minEditorWidth) > leftSideWdith) {
                // 左侧固定
                if(!isPin(leftPin)) leftPin.click();
                if(isPin(rightPin)) {rightPin.click();hideDock('right');}
            } else {
                // 两侧都不固定(隐藏)
                if(isPin(leftPin)) {leftPin.click();hideDock('left')}
                if(isPin(rightPin)) {rightPin.click();hideDock('right');}
            }
        }).observe(layoutCenter);
    }, 2000);
    

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • wilsons 4 2 赞同 5 评论

    其实我觉得不用这么麻烦,即无需这么多判断,只需判断窗口宽度即可。

    根据你的描述,大概有 3 种状态,1 两侧固定 2 仅左侧固定 3 两侧都不固定(隐藏)

    那么只需要判断 1 和 2 即可,且仅需判断窗口宽度即可,临界值根据自己需要修改。

    比如以下代码,经测试我觉得可以满足需求。

    // 窗口宽度自适应变化(自动显示/隐藏侧边栏)
    window.addEventListener('resize', () => {
        // 两侧固定阈值,当窗口大于这个值时两侧固定(可根据自己需要修改)
        const bothFixedBreakpoint = 1100;
      
        // 左侧固定阈值,当窗口大于这个值时左侧固定(可根据自己需要修改)
        const leftSideFixedBreakpoint = 800;
      
        if(!!document.getElementById("sidebar")) return; // 手机版跳过
        const leftPin = document.querySelector('#dockLeft .dock__item--pin');
        const rightPin = document.querySelector('#dockRight .dock__item--pin');
        const isPin = (pin) => !!pin.querySelector('svg use[*|href="#iconUnpin"]');
        const hideDock = (dock) => {
            let sign = '', dockEl;
            if(dock === 'left') {
                sign = '-';
                dockEl = document.querySelector('#layouts .layout__dockl.layout--float');
            } else {
                dockEl = document.querySelector('#layouts .layout__dockr.layout--float');
            }
            if(dockEl && !dockEl.style.transform) {
                // https://github.com/siyuan-note/siyuan/blob/5f0f4e3ba6aabd5af26f9879695e5b9b796b5fb9/app/src/layout/dock/index.ts#L478
                dockEl.style.transform = `translateX(${sign}${dockEl.clientWidth + 8}px)`;
                dockEl.style.opacity = "0";
            }
        }
        if(window.innerWidth > bothFixedBreakpoint) {
            // 两侧固定
            if(!isPin(leftPin)) leftPin.click();
            if(!isPin(rightPin)) rightPin.click();
        } else if(window.innerWidth > leftSideFixedBreakpoint) {
            // 左侧固定
            if(!isPin(leftPin)) leftPin.click();
            if(isPin(rightPin)) {rightPin.click();hideDock('right');}
        } else {
            // 两侧都不固定(隐藏)
            if(isPin(leftPin)) {leftPin.click();hideDock('left')}
            if(isPin(rightPin)) {rightPin.click();hideDock('right');}
        }
    });
    

    r159.gif


    版本 2,可能这个版本更符合你的需求

    // 窗口宽度自适应变化(自动显示/隐藏侧边栏)
    window.addEventListener('resize', () => {
        // 编辑器最小宽度,当窗口宽度大于编辑器最小宽度+两侧宽度时全固定,当仅容纳一个侧栏时左侧固定,否则全隐藏
        const minEditorWidth = 600;
    
        if(!!document.getElementById("sidebar")) return; // 手机版跳过
        const leftPin = document.querySelector('#dockLeft .dock__item--pin');
        const rightPin = document.querySelector('#dockRight .dock__item--pin');
        const isPin = (pin) => !!pin.querySelector('svg use[*|href="#iconUnpin"]');
        const hideDock = (dock) => {
            let sign = '', dockEl;
            if(dock === 'left') {
                sign = '-';
                dockEl = document.querySelector('#layouts .layout__dockl.layout--float');
            } else {
                dockEl = document.querySelector('#layouts .layout__dockr.layout--float');
            }
            if(dockEl && !dockEl.style.transform) {
                // https://github.com/siyuan-note/siyuan/blob/5f0f4e3ba6aabd5af26f9879695e5b9b796b5fb9/app/src/layout/dock/index.ts#L478
                dockEl.style.transform = `translateX(${sign}${dockEl.clientWidth + 8}px)`;
                dockEl.style.opacity = "0";
            }
        }
        const leftSideWdith = document.querySelector('#layouts .layout__dockl')?.offsetWidth || 0;
        const rightSideWidth = document.querySelector('#layouts .layout__dockr')?.offsetWidth || 0;
        if((window.innerWidth -  minEditorWidth) > (leftSideWdith + rightSideWidth)) {
            // 两侧固定
            if(!isPin(leftPin)) leftPin.click();
            if(!isPin(rightPin)) rightPin.click();
        } else if((window.innerWidth -  minEditorWidth) > leftSideWdith) {
            // 左侧固定
            if(!isPin(leftPin)) leftPin.click();
            if(isPin(rightPin)) {rightPin.click();hideDock('right');}
        } else {
            // 两侧都不固定(隐藏)
            if(isPin(leftPin)) {leftPin.click();hideDock('left')}
            if(isPin(rightPin)) {rightPin.click();hideDock('right');}
        }
    });
    

    版本 3,兼容性更好,兼容直接拖动侧边栏的情况

    // 窗口宽度自适应变化(自动显示/隐藏侧边栏)
    setTimeout(() => {
        // 编辑器最小宽度,当窗口宽度大于编辑器最小宽度+两侧宽度时全固定,当仅容纳一个侧栏时左侧固定,否则全隐藏
        const minEditorWidth = 600;
        
        const layoutCenter = document.querySelector('.layout__center');
        if(!layoutCenter) return;
        new ResizeObserver((entries) => {
            const leftPin = document.querySelector('#dockLeft .dock__item--pin');
            const rightPin = document.querySelector('#dockRight .dock__item--pin');
            const isPin = (pin) => !!pin.querySelector('svg use[*|href="#iconUnpin"]');
            const hideDock = (dock) => {
                let sign = '', dockEl;
                if(dock === 'left') {
                    sign = '-';
                    dockEl = document.querySelector('#layouts .layout__dockl.layout--float');
                } else {
                    dockEl = document.querySelector('#layouts .layout__dockr.layout--float');
                }
                if(dockEl && !dockEl.style.transform) {
                    // https://github.com/siyuan-note/siyuan/blob/5f0f4e3ba6aabd5af26f9879695e5b9b796b5fb9/app/src/layout/dock/index.ts#L478
                    dockEl.style.transform = `translateX(${sign}${dockEl.clientWidth + 8}px)`;
                    dockEl.style.opacity = "0";
                }
            }
            const leftSideWdith = document.querySelector('#layouts .layout__dockl')?.offsetWidth || 0;
            const rightSideWidth = document.querySelector('#layouts .layout__dockr')?.offsetWidth || 0;
            if((window.innerWidth -  minEditorWidth) > (leftSideWdith + rightSideWidth)) {
                // 两侧固定
                if(!isPin(leftPin)) leftPin.click();
                if(!isPin(rightPin)) rightPin.click();
            } else if((window.innerWidth -  minEditorWidth) > leftSideWdith) {
                // 左侧固定
                if(!isPin(leftPin)) leftPin.click();
                if(isPin(rightPin)) {rightPin.click();hideDock('right');}
            } else {
                // 两侧都不固定(隐藏)
                if(isPin(leftPin)) {leftPin.click();hideDock('left')}
                if(isPin(rightPin)) {rightPin.click();hideDock('right');}
            }
        }).observe(layoutCenter);
    }, 2000);
    
    3 操作
    wilsons 在 2025-07-28 20:56:31 更新了该回帖
    wilsons 在 2025-07-28 20:42:36 更新了该回帖
    wilsons 在 2025-07-28 18:56:00 更新了该回帖
    很完美,感谢大佬!
    Adaxi
    我用的 2K 显示器,调整为 1850 和 1350 比较合适
    Adaxi
    @Adaxi OK,新增了版本 2,可能这个版本更符合你的需求,只需要设置编辑器最小宽度即可。
    wilsons 1 1 赞同
    @wilsons 感谢大佬!!!
    Adaxi
    配合自适应宽度超好用的
    Adaxi 1 赞同
  • 其他回帖
  • att88

    配合上 altsnap 简直太好用了,感谢!

  • att88

    好用,上来点个赞

  • Adaxi
    作者

    建议这个功能被官方收录,真的很好用。

推荐标签 标签

  • WiFiDog

    WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。

    1 引用 • 7 回帖 • 633 关注
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    173 引用 • 1559 回帖
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    110 引用 • 153 回帖
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 563 关注
  • 千千插件

    千千块(自定义块 css 和 js)
    可以用 ai 提示词来无限创作思源笔记

    32 引用 • 69 回帖
  • Ubuntu

    Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。

    127 引用 • 169 回帖
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    105 引用 • 908 回帖
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    33 引用 • 108 回帖
  • WebClipper

    Web Clipper 是一款浏览器剪藏扩展,它可以帮助你把网页内容剪藏到本地。

    3 引用 • 9 回帖 • 2 关注
  • WebSocket

    WebSocket 是 HTML5 中定义的一种新协议,它实现了浏览器与服务器之间的全双工通信(full-duplex)。

    48 引用 • 206 回帖 • 284 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖 • 1 关注
  • 阿里巴巴

    阿里巴巴网络技术有限公司(简称:阿里巴巴集团)是以曾担任英语教师的马云为首的 18 人,于 1999 年在中国杭州创立,他们相信互联网能够创造公平的竞争环境,让小企业通过创新与科技扩展业务,并在参与国内或全球市场竞争时处于更有利的位置。

    43 引用 • 221 回帖 • 11 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖
  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    151 引用 • 257 回帖 • 1 关注
  • GraphQL

    GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。

    4 引用 • 3 回帖 • 11 关注
  • Follow
    4 引用 • 13 回帖 • 19 关注
  • sts
    2 引用 • 2 回帖 • 260 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 691 关注
  • 电影

    这是一个不能说的秘密。

    125 引用 • 610 回帖
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 545 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    15 引用 • 7 回帖
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 123 关注
  • Unity

    Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    27 引用 • 7 回帖 • 92 关注
  • Google

    Google(Google Inc.,NASDAQ:GOOG)是一家美国上市公司(公有股份公司),于 1998 年 9 月 7 日以私有股份公司的形式创立,设计并管理一个互联网搜索引擎。Google 公司的总部称作“Googleplex”,它位于加利福尼亚山景城。Google 目前被公认为是全球规模最大的搜索引擎,它提供了简单易用的免费服务。不作恶(Don't be evil)是谷歌公司的一项非正式的公司口号。

    51 引用 • 200 回帖 • 2 关注
  • 心情

    心是产生任何想法的源泉,心本体会陷入到对自己本体不能理解的状态中,因为心能产生任何想法,不能分出对错,不能分出自己。

    59 引用 • 369 回帖 • 1 关注
  • Shell

    Shell 脚本与 Windows/Dos 下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比 Windows 下的批处理更强大,比用其他编程程序编辑的程序效率更高,因为它使用了 Linux/Unix 下的命令。

    126 引用 • 83 回帖 • 1 关注