[js] 双击展开 & 展开全部 _release_v1.0

[js 片段] 双击展开&展开全部_release_v1.0

功能介绍

  1. 双击节点展开/折叠 (DOUBLE_SWITCH 控制)
  2. 点击折叠按钮, 如果是从折叠到展开, 则自动展开所有子节点 (UNFOLD_ALL_SWITCH 控制)
/* ### [js片段] 双击展开&展开全部_release_v1.0 #### 功能介绍 1. 双击节点展开/折叠 (DOUBLE_SWITCH 控制) 2. 点击折叠按钮, 如果是从折叠到展开, 则自动展开所有子节点 (UNFOLD_ALL_SWITCH 控制) */ (() => { /***************************自主配置begin**************************************/ // 修改配置后, 需要刷新页面(设置->快捷键->刷新), 否则会有问题 const CONFIG = { DOUBLE_SWITCH : true, // 是否启用双击展开/折叠 DOUBLE_INTERVAL : 200, // 双击间隔时间 毫秒 UNFOLD_ALL_SWITCH : true, // 是否替换原始折叠按钮功能, 改为一键全部展开 UNFOLD_INTERVAL : 200, // 自动展开的点击的间隔, 如果你感觉展开很慢, 可适当调小 // 请注意 如果过于小, 可能会导致文档树渲染异常 }; /***************************自主配置end****************************************/ // 生成唯一ID用于日志标识 const SESSION_ID = Date.now(); // 点击类型枚举 const CLICK_TYPE = { INVALID: 0, // 无效的点击 NODE : 1, // 点击节点 ARROW : 2, // 点击折叠按钮 BOOK : 3, // 点击笔记本 }; // 工具函数 const utils = { log(...args) { console.log(`[${SESSION_ID}]:`, ...args); }, sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }, getTagName(element) { return element?.tagName?.toLowerCase() || ''; } }; // 树节点处理类 class TreeHandler { constructor() { this.clickQueue = []; // 点击队列 this.isProcessing = false; // 是否正在处理队列 this.doubleClickFlag = false; // 双击标记 this.lastClickTime = 0; // 上次点击时间 } // 获取当前元素对应的种类 getClickType(element) { if (!element) return CLICK_TYPE.INVALID; let target = element; let tagName = utils.getTagName(target); // 处理SVG元素 if (tagName === 'use') { target = element.parentElement.parentElement; } else if (tagName === 'svg') { target = element.parentElement; } tagName = utils.getTagName(target); if (tagName === 'ul') { return CLICK_TYPE.INVALID; } else if (tagName === 'li' && target.getAttribute('data-path') === '/') { return CLICK_TYPE.BOOK; } else if (tagName === 'li') { return CLICK_TYPE.NODE; } else if (tagName === 'span') { if (target.classList.contains('b3-list-item__toggle')) { return CLICK_TYPE.ARROW; } else if (target.classList.contains('b3-list-item__text') && target.parentElement.getAttribute('data-path') === '/') { return CLICK_TYPE.BOOK; } else if (target.classList.contains('b3-list-item__text')) { return CLICK_TYPE.NODE; } } return CLICK_TYPE.INVALID; } // 获取折叠按钮的span元素, 前提: element必须是折叠按钮 getArrowSpan(element) { let target = element; let tagName = utils.getTagName(target); if (tagName === 'use') { target = element.parentElement.parentElement; } else if (tagName === 'svg') { target = element.parentElement; } tagName = utils.getTagName(target); if (tagName === 'span' && target.classList.contains('b3-list-item__toggle')) { return target; } return null; } // 点击节点对应的折叠按钮, 前提: element必须是点击的节点 clickArrowButton(element) { const li = utils.getTagName(element) === 'span' ? element.parentElement : element; if (utils.getTagName(li) !== 'li') return; li.querySelector('span.b3-list-item__toggle:not(.fn__hidden)')?.click(); } // 遍历 element下所有直接子节点的折叠按钮, 并点击 async unfoldChildNodes(element) { const arrowSpan = this.getArrowSpan(element); if (!arrowSpan) return; if (!arrowSpan.querySelector('svg').classList.contains('b3-list-item__arrow--open')) { return; } // 点击了折叠按钮, 且是展开的 utils.log("点击了折叠按钮, 且是展开的", arrowSpan); const childNodes = Array.from(arrowSpan.parentElement.nextElementSibling.children); childNodes.forEach(node => { node.querySelector('span.b3-list-item__toggle:not(.fn__hidden)')?.click(); }); } // 触发处理 点击队列 async processClickQueue() { if (this.isProcessing) return; this.isProcessing = true; while (this.clickQueue.length) { const action = this.clickQueue.shift(); await action(); } this.isProcessing = false; } // 处理鼠标点击事件 handleClickEvent(event) { const element = event.target; const clickType = this.getClickType(element); utils.log("点击了", clickType, element); // 处理双击, 系统自带的双击监测事件, 有点问题, 所以自己实现一个 const currentTime = Date.now(); if (CONFIG.DOUBLE_SWITCH && currentTime - this.lastClickTime < CONFIG.DOUBLE_INTERVAL && clickType === CLICK_TYPE.NODE) { utils.log("双击展开/折叠触发"); this.doubleClickFlag = true; this.clickArrowButton(element); } this.lastClickTime = currentTime; // 处理折叠按钮点击 if (CONFIG.UNFOLD_ALL_SWITCH && clickType === CLICK_TYPE.ARROW) { if (this.doubleClickFlag) { this.doubleClickFlag = false; return; } utils.log("点击折叠按钮处理", element); this.clickQueue.push(async () => { await utils.sleep(CONFIG.UNFOLD_INTERVAL); await this.unfoldChildNodes(element); }); this.processClickQueue(); } } } // 初始化并监听点击事件 let initInterval = setInterval(() => { const treeContainer = document.querySelector('.sy__file>.fn__flex-1'); if (treeContainer) { clearInterval(initInterval); const handler = new TreeHandler(); treeContainer.addEventListener('click', e => handler.handleClickEvent(e), true); } }, 200); })();
  • 思源笔记

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

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

    25765 引用 • 106651 回帖
  • 代码片段

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

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

    174 引用 • 1220 回帖 • 4 关注

相关帖子

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
  • EpicJay

    这个好

  • 其他回帖
  • EpicJay

    不过还是改一下,改成双击一键展开,单击折叠按钮还是原来那样打开一级比较好,毕竟一键展开并不是时时会用到,但是文档树展开下一层节点却是高频率事件

  • 很实用,感谢

推荐标签 标签

  • ngrok

    ngrok 是一个反向代理,通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道。

    7 引用 • 63 回帖 • 652 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 1 关注
  • 开源中国

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

    7 引用 • 86 回帖
  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    42 引用 • 130 回帖 • 250 关注
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    499 引用 • 1395 回帖 • 244 关注
  • 工具

    子曰:“工欲善其事,必先利其器。”

    298 引用 • 763 回帖 • 1 关注
  • 程序员

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

    588 引用 • 3528 回帖
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 726 关注
  • Bootstrap

    Bootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter 的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS / HTML 框架。

    18 引用 • 33 回帖 • 646 关注
  • gRpc
    11 引用 • 9 回帖 • 94 关注
  • 一些有用的避坑指南。

    69 引用 • 93 回帖
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    89 引用 • 150 回帖
  • V2EX

    V2EX 是创意工作者们的社区。这里目前汇聚了超过 400,000 名主要来自互联网行业、游戏行业和媒体行业的创意工作者。V2EX 希望能够成为创意工作者们的生活和事业的一部分。

    16 引用 • 236 回帖 • 253 关注
  • 国际化

    i18n(其来源是英文单词 internationalization 的首末字符 i 和 n,18 为中间的字符数)是“国际化”的简称。对程序来说,国际化是指在不修改代码的情况下,能根据不同语言及地区显示相应的界面。

    8 引用 • 26 回帖 • 1 关注
  • RemNote
    2 引用 • 16 回帖 • 15 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 3 关注
  • ReactiveX

    ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。

    1 引用 • 2 回帖 • 184 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 347 关注
  • 阿里云

    阿里云是阿里巴巴集团旗下公司,是全球领先的云计算及人工智能科技公司。提供云服务器、云数据库、云安全等云计算服务,以及大数据、人工智能服务、精准定制基于场景的行业解决方案。

    84 引用 • 324 回帖
  • AWS
    11 引用 • 28 回帖 • 3 关注
  • 心情

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

    59 引用 • 369 回帖
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 58 关注
  • Markdown

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

    171 引用 • 1537 回帖
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    178 引用 • 3866 回帖
  • abitmean

    有点意思就行了

    34 关注