[js] 希望尽量折叠大纲

之前在社区发布一个关于折叠大纲的代码求助(希望鼠标悬浮自动展开大纲 - 链滴),很高兴看到有热心的人进行帮助,但是最近复习的时候,发现有一点需要优化一下

大纲折叠可以方便我们快速获取文章的大致结构,方便我们去进行定位,但是当我们复习的时候,也就是鼠标点击大纲跳到对应的标题时,如果想回去大纲再进行点击,就需要再次寻找标题对应的大纲,然而此时大纲的加深效果(光标对应标题的大纲定位效果)已经消失,寻找对应的标题就需要重新一个字一个字的核对,这对复习效果造成极大的不便,望请大佬们帮我修改下代码片段

这是目前的效果

image.png

这是我理想的效果

image.png

示例代码

// see https://ld246.com/article/1727096963532
(async ()=>{
    whenElementExist('.sy__outline > .fn__flex-1').then(async el => {
        // 监听大纲标题被添加
        observeChildAddition(el, node => {
            return node.tagName.toLowerCase() === 'ul' &&
                        node.classList.contains('b3-list') && 
                        node.querySelector('.b3-list-item')
        }, uls => {
            // 获取大纲列表
            const ul = uls[0];
            // 遍历大纲第一级子元素
            Array.from(ul.children).forEach(item => {
                // 初始时,仅打开第一级
                if(item.tagName === 'LI') {
                    const toggleBtn = item.querySelector('.b3-list-item__toggle');
                    const svg = toggleBtn?.querySelector('svg.b3-list-item__arrow');
                    if(!svg.classList.contains('b3-list-item__arrow--open')) {
                        svg.classList.add('b3-list-item__arrow--open');
                    }
                }
                if(item.tagName === 'UL') {
                    if(item.classList.contains('fn__none')) {
                        item.remove('fn__none');
                    }
                    // 初始时,隐藏第一级下面的后代元素
                    itemsShow(item, false);
                }
                // 监听大纲鼠标移入事件
                const ul = item.tagName === 'LI' ? item.nextElementSibling : item;
                item.addEventListener('mouseenter', (event) => {
                    if(!ul || ul?.tagName !== 'UL') return;
                    // 鼠标移入显示第一级后面的后代元素
                    itemsShow(ul, true);
                })
                // 监听大纲鼠标移出事件
                item.addEventListener('mouseleave', (event) => {
                    if(!ul || ul?.tagName !== 'UL') return;
                    // 鼠标移出隐藏第一级后面的后代元素
                    itemsShow(ul, false);
                });
            });
        });
    });
    // 动态显示隐藏子标题
    function itemsShow(ul, isOpen) {
        if(isOpen){
           const svgs = ul.querySelectorAll('span.b3-list-item__toggle svg:not(.b3-list-item__arrow--open)');
            svgs.forEach(item => {
                item.classList.add('b3-list-item__arrow--open');
            });
            const uls = ul.querySelectorAll('ul.fn__none');
            uls.forEach(item => {
                item.classList.remove('fn__none');
            });
        } else {
            const svgs = ul.querySelectorAll('span.b3-list-item__toggle svg.b3-list-item__arrow--open');
            svgs.forEach(item => {
                item.classList.remove('b3-list-item__arrow--open');
            });
            const uls = ul.querySelectorAll('ul:not(.fn__none)');
            uls.forEach(item => {
                item.classList.add('fn__none');
            });
        }
    }
    function observeChildAddition(el, filter, handler) {
        // 配置观察器选项
        const config = { attributes: false, childList: true, subtree: false };
        // 定义回调函数
        const callback = function(mutationsList, observer) {
            // 遍历 mutation 列表
            for (let mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    console.log(mutation, 111);
                    // 查找新增加的具有类名 'b3-list' 的 ul 元素
                    const newULs = Array.from(mutation.addedNodes).filter(node => node.nodeType === Node.ELEMENT_NODE && filter(node));
                    // 如果有新的 ul 元素被添加,则调用处理函数
                    if(newULs.length > 0) {
                        handler(newULs);
                    }
                }
            }
        };
        // 创建一个新的 MutationObserver 实例
        const observer = new MutationObserver(callback);
        // 开始观察目标节点
        observer.observe(el, config);
        // 返回一个函数来停止观察
        return () => {
            observer.disconnect();
        };
    }
    // 等待元素渲染完成后执行
    function whenElementExist(selector, bySetTimeout = false, delay = 40) {
        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 {
                    if (bySetTimeout) {
                        setTimeout(checkForElement, delay);
                    } else {
                        requestAnimationFrame(checkForElement);
                    }
                }
            };
            checkForElement();
        });
    }
})();
  • 思源笔记

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

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

    21853 引用 • 86932 回帖
  • 代码片段

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

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

    50 引用 • 275 回帖
  • Q&A

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

    7860 引用 • 35943 回帖 • 180 关注
1 操作
CongSec 在 2024-10-23 20:16:25 置顶了该帖

相关帖子

被采纳的回答

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
CongSec
没事别关注我, 因为发帖需要更多积分 广州

推荐标签 标签

  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    192 引用 • 291 回帖 • 401 关注
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 53 关注
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 98 关注
  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    490 引用 • 915 回帖
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    139 引用 • 269 回帖 • 53 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 506 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    180 引用 • 400 回帖
  • 大数据

    大数据(big data)是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。

    93 引用 • 113 回帖
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    942 引用 • 1459 回帖 • 39 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 212 关注
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    29 引用 • 66 回帖 • 2 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    62 引用 • 289 回帖
  • Hibernate

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

    39 引用 • 103 回帖 • 704 关注
  • IBM

    IBM(国际商业机器公司)或万国商业机器公司,简称 IBM(International Business Machines Corporation),总公司在纽约州阿蒙克市。1911 年托马斯·沃森创立于美国,是全球最大的信息技术和业务解决方案公司,拥有全球雇员 30 多万人,业务遍及 160 多个国家和地区。

    17 引用 • 53 回帖 • 129 关注
  • 周末

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

    14 引用 • 297 回帖
  • Swagger

    Swagger 是一款非常流行的 API 开发工具,它遵循 OpenAPI Specification(这是一种通用的、和编程语言无关的 API 描述规范)。Swagger 贯穿整个 API 生命周期,如 API 的设计、编写文档、测试和部署。

    26 引用 • 35 回帖
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    676 引用 • 535 回帖
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 639 关注
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖 • 6 关注
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    170 引用 • 414 回帖 • 383 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 1 关注
  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    52 引用 • 190 回帖 • 5 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    179 引用 • 407 回帖 • 489 关注
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    107 引用 • 295 回帖
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    98 引用 • 344 回帖
  • SSL

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

    70 引用 • 193 回帖 • 442 关注
  • 程序员

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

    564 引用 • 3532 回帖 • 2 关注