求 js 代码,只展开当前活动页签的文档树

本贴最后更新于 321 天前,其中的信息可能已经时移俗易

求 js 代码,在使用“始终定位打开的文档”功能时,只展开当前文档的文档树,自动关闭(不展开)其它无关的文档树。例如:

有笔记本 A,内有文档 A1,子文档 A2、子子文档 A3。

有笔记本 B,内有文档 B1,子文档 B2、子子文档 B3。

已将文档 A2、B2 打开,显示在上方页签栏中。

启用“始终定位打开的文档”功能。

点击 A2 页签,左侧文档树自动展开,并定位到 A-A1-A2;A3 列表不展开

点击 B2 页签,左侧文档树自动展开,定位到 B-B1-B2;同时关闭 A-A1-A2 文档树的展开状态,恢复到笔记本 A。也就是仅保留当前活动页签对应的文档树是展开状态,其它文档树分支全部关闭,包括当前活动文档的子文档树。

  • 思源笔记

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

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

    25151 引用 • 103730 回帖
  • Q&A

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

    9601 引用 • 43679 回帖 • 97 关注
1 操作
wenbocn 在 2024-06-02 13:54:35 更新了该帖
被采纳的回答
  • wilsons 1

    时隔 1 个多月,你解决了吗?相见即是有缘,看到此贴感觉有些思路,就尝试写了下,实现了这个功能。由于刚接触思源,对思源 api 还不是很了解,所以选择用 js 代码片段实现,纯原生 js 实现,方法比较笨,勉强能用。

    不足之处也请论坛里的大佬们多多批评指正!

    效果如下:

    t2.gif

    完整代码如下:

    // 等待标签页容器渲染完成后开始监听 whenElementExist('.layout__center').then(async element => { // 等待笔记列表加载完毕 await sleep(40); // 监听页签切换事件 observeTabChanged(element, (tab) => { // 折叠所有笔记,然后定位当前笔记 collapseAllBooksThenFocusCurrentBook(element, tab); }); }); // 折叠所有笔记,然后定位当前笔记 async function collapseAllBooksThenFocusCurrentBook(element, tab) { // 等待激活文档加载完毕 await whenElementExist(()=>{ const content = element.querySelector('.layout-tab-container [data-id="'+tab.getAttribute("data-id")+'"]'); return content && content.getAttribute("data-loading") === "finished"; }); // 折叠所有笔记 document.querySelectorAll("ul.b3-list[data-url]").forEach(async book => { const bookName = tab.getAttribute("aria-label")?.split('/')[0]; if(bookName) { // 如果在本笔记中则不再折叠 const bookText = book.querySelector('li[data-type="navigation-root"] span.b3-list-item__text')?.innerText; if(bookText === bookName) { return; } } // 折叠笔记 const bookArrowBtn = book.querySelector('li[data-type="navigation-root"] span.b3-list-item__toggle'); if (bookArrowBtn && bookArrowBtn.firstElementChild.classList.contains("b3-list-item__arrow--open")) { bookArrowBtn.click(); } }); // 定位当前笔记 document.querySelector(".layout-tab-container .block__icons span[data-type=focus]")?.click(); } // 监听页签切换事件 function observeTabChanged(parentNode, callback) { // 创建一个回调函数来处理观察到的变化 const observerCallback = function(mutationsList, observer) { // 用常规方式遍历 mutationsList 中的每一个 mutation for (let mutation of mutationsList) { // 属性被修改 if (mutation.type === 'attributes' && mutation.attributeName === 'class') { const element = mutation.target; if (element.tagName.toLowerCase() === 'li' && element.getAttribute('data-type') === 'tab-header' && element.classList.contains('item--focus')) { if(typeof callback === 'function') callback(element); } } // 如果有新的子节点被添加 if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'li') { if (node.getAttribute('data-type') === 'tab-header' && node.classList.contains('item--focus')) { if(typeof callback === 'function') callback(node); } } }); } } }; // 创建一个观察器实例并传入回调函数 const observer = new MutationObserver(observerCallback); // 配置观察器:传递一个对象来指定观察器的行为 const config = { attributes: true, attributeFilter: ['class'], childList: true, subtree: true }; // 开始观察目标节点 observer.observe(parentNode, config); // 返回一个函数,用于停止观察 return function stopObserving() { observer.disconnect(); }; } // 延迟执行 function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 等待元素渲染完成后执行 function whenElementExist(selector) { return new Promise(resolve => { const checkForElement = () => { let element = null; if (typeof selector === 'function') { element = selector(); } else { element = document.querySelector(selector); } if (element) { resolve(element); } else { requestAnimationFrame(checkForElement); } }; checkForElement(); }); }

    使用方法:

    设置 》外观 》代码片段 》js 中新增加代码片段,然后把上面的代码粘贴过去即可。

    image.png

优质回帖
  • wilsons 2

    @wenbocn 我已完美实现了不闪动的版本,并且改进了官方定位,在未打开目录树时,左侧 dock 区目录树显示隐藏按钮样式会获取焦点的 bug

    效果如下

    t6.gif

    完整代码如下

    由于链滴代码长度有限制,只能放到 jsrun 了。

  • wilsons 2

    确实是焦点问题,楼主主要想要的是定位文档树,且定位和展开关闭目录使用了模拟点击,这些操作都会使当前文档的焦点移动到目录树上,导致大纲还没来得及切换就失去焦点文档了。

    其实,定位文档树也是要获得焦点的,这和文档焦点矛盾,具体怎么做看自己需求,是希望最后焦点落在哪里?

    如果想最终焦点落在文档树上,可以在 focusCurrentDocInTrees 函数的开头加上 sleep(120),等待大纲切换完毕再定位

    image.png

    image.png

    如果想最终焦点落在文档上,则在 focusCurrentDocInTrees 函数的末尾加上以下代码,定位文档树后恢复文档焦点

    // 文档树模拟点击后恢复文档窗口(编辑器)的焦点(最终焦点在文档) whenElementExist('.layout__tab--active .b3-list--background .b3-list-item--focus').then(() => { const activepPotyle = document.querySelector('[data-type="wnd"].layout__wnd--active .protyle:not(.fn__none)')||document.querySelector('[data-type="wnd"] .protyle:not(.fn__none)'); if(activepPotyle) activepPotyle.click(); });

    image.png

    image.png

    这是我修改后的代码,只需要配置这个参数即可

    // 最终焦点落在文档树还是文档上? tree 文档树,doc 文档
    const lastFoucsIn = 'doc';

    https://gitee.com/wish163/mysoft/blob/main/%E6%80%9D%E6%BA%90/%E5%8F%AA%E5%B1%95%E5%BC%80%E5%BD%93%E5%89%8D%E6%B4%BB%E5%8A%A8%E9%A1%B5%E7%AD%BE%E7%9A%84%E6%96%87%E6%A1%A3%E6%A0%91.js

  • player 1 赞同

    ToolbarBox

    这里有除了自动定位的其他代码。 自动定位就做个监听, 针对 class layout__wnd--active ,发现有 tab 的变化,就触发一次,locateDoc() 即可。

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
  • 哈哈哈,自然是搜索时遇到的

  • 其他回帖
  • 你可以录屏看看,我这边并没有发现你说的问题,可能和你的样式有关。

    看我这边效果

    t7.gif

    仔细看,你会发现,当文档树隐藏时,切换标签,文档树显示隐藏按钮的焦点会闪动下,这就是切换标签时,默认的官方定位会让这个按钮聚焦,而我动态把这个聚焦给去掉了。

    1 回复
  • wilsons 2

    确实是焦点问题,楼主主要想要的是定位文档树,且定位和展开关闭目录使用了模拟点击,这些操作都会使当前文档的焦点移动到目录树上,导致大纲还没来得及切换就失去焦点文档了。

    其实,定位文档树也是要获得焦点的,这和文档焦点矛盾,具体怎么做看自己需求,是希望最后焦点落在哪里?

    如果想最终焦点落在文档树上,可以在 focusCurrentDocInTrees 函数的开头加上 sleep(120),等待大纲切换完毕再定位

    image.png

    image.png

    如果想最终焦点落在文档上,则在 focusCurrentDocInTrees 函数的末尾加上以下代码,定位文档树后恢复文档焦点

    // 文档树模拟点击后恢复文档窗口(编辑器)的焦点(最终焦点在文档) whenElementExist('.layout__tab--active .b3-list--background .b3-list-item--focus').then(() => { const activepPotyle = document.querySelector('[data-type="wnd"].layout__wnd--active .protyle:not(.fn__none)')||document.querySelector('[data-type="wnd"] .protyle:not(.fn__none)'); if(activepPotyle) activepPotyle.click(); });

    image.png

    image.png

    这是我修改后的代码,只需要配置这个参数即可

    // 最终焦点落在文档树还是文档上? tree 文档树,doc 文档
    const lastFoucsIn = 'doc';

    https://gitee.com/wish163/mysoft/blob/main/%E6%80%9D%E6%BA%90/%E5%8F%AA%E5%B1%95%E5%BC%80%E5%BD%93%E5%89%8D%E6%B4%BB%E5%8A%A8%E9%A1%B5%E7%AD%BE%E7%9A%84%E6%96%87%E6%A1%A3%E6%A0%91.js

    2 回复
    1 操作
    wilsons 在 2025-03-14 20:25:34 更新了该回帖
  • 大佬,我在使用该 JS 片段时,发现文档切换后大纲目录不会随之切换。关闭 JS 片段后,大纲目录就正常切换了。请问可以优化下吗?

    使用时思源版本为 v3.1.24,默认主题,关闭了所有插件,仅开启该 JS 片段。测试.gif

  • 查看全部回帖

推荐标签 标签

  • H2

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

    11 引用 • 54 回帖 • 669 关注
  • SSL

    SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加密。

    70 引用 • 193 回帖 • 412 关注
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    286 引用 • 248 回帖
  • Vue.js

    Vue.js(读音 /vju ː/,类似于 view)是一个构建数据驱动的 Web 界面库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

    267 引用 • 666 回帖 • 1 关注
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    588 引用 • 3538 回帖 • 1 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    36 引用 • 37 回帖 • 544 关注
  • AWS
    11 引用 • 28 回帖 • 13 关注
  • 游戏

    沉迷游戏伤身,强撸灰飞烟灭。

    181 引用 • 821 回帖 • 1 关注
  • 博客

    记录并分享人生的经历。

    273 引用 • 2388 回帖
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖 • 2 关注
  • 强迫症

    强迫症(OCD)属于焦虑障碍的一种类型,是一组以强迫思维和强迫行为为主要临床表现的神经精神疾病,其特点为有意识的强迫和反强迫并存,一些毫无意义、甚至违背自己意愿的想法或冲动反反复复侵入患者的日常生活。

    15 引用 • 161 回帖 • 2 关注
  • WiFiDog

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

    1 引用 • 7 回帖 • 608 关注
  • Word
    13 引用 • 40 回帖
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1441 引用 • 10068 回帖 • 495 关注
  • 资讯

    资讯是用户因为及时地获得它并利用它而能够在相对短的时间内给自己带来价值的信息,资讯有时效性和地域性。

    56 引用 • 85 回帖 • 2 关注
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    12 引用 • 54 回帖 • 167 关注
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    952 引用 • 944 回帖 • 1 关注
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 84 关注
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖
  • FFmpeg

    FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。

    23 引用 • 32 回帖
  • Outlook
    1 引用 • 5 回帖
  • RemNote
    2 引用 • 16 回帖 • 7 关注
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    184 引用 • 1015 回帖
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    79 引用 • 431 回帖
  • 学习

    “梦想从学习开始,事业从实践起步” —— 习近平

    172 引用 • 516 回帖
  • Netty

    Netty 是一个基于 NIO 的客户端-服务器编程框架,使用 Netty 可以让你快速、简单地开发出一个可维护、高性能的网络应用,例如实现了某种协议的客户、服务端应用。

    49 引用 • 33 回帖 • 36 关注