[js] 显示折叠列表节点下的子节点数

功能说明

安装启用代码片段后,将鼠标移动到折叠状态下的节点时,会显示其直接子节点数和总节点数。

如图

PixPin20250309122549.png

代码片段

(function() { // 避免重复处理的标志 const processedClass = 'child-count-processed'; // 函数:计算节点下的直接子节点和总子节点数量 function countChildren(nodeListItem) { if (!nodeListItem) return { directCount: 0, totalCount: 0 }; // 查找节点内的列表元素 const listElement = Array.from(nodeListItem.children).find(child => child.dataset.type === 'NodeList' && child.classList.contains('list')); if (!listElement) return { directCount: 0, totalCount: 0 }; // 获取直接子节点,只找 NodeListItem const directChildren = Array.from(listElement.children).filter(child => child.dataset.type === 'NodeListItem'); let directCount = directChildren.length; let totalCount = directCount; // 计算每个直接子节点的子节点数量 directChildren.forEach(child => { const childCounts = countChildren(child); totalCount += childCounts.totalCount; }); return { directCount, totalCount }; } // 函数:处理所有节点(折叠和非折叠) function processAllNodes() { // 获取所有列表项节点 const allNodes = document.querySelectorAll('[data-type="NodeListItem"]'); allNodes.forEach(node => { try { // 检查节点是否处于折叠状态 const isFolded = node.getAttribute('fold') === '1'; if (isFolded) { // 如果是折叠状态,添加子节点计数 const { directCount, totalCount } = countChildren(node); // 设置title属性 if (directCount > 0) { if (directCount === totalCount) { node.title = `子节点: ${directCount}`; } else { node.title = `直接子节点: ${directCount}, 总子节点: ${totalCount}`; } node.classList.add(processedClass); } } else { // 如果不是折叠状态,移除title和处理标记 if (node.hasAttribute('title') && node.classList.contains(processedClass)) { node.removeAttribute('title'); node.classList.remove(processedClass); } } } catch (error) { console.error('处理节点时出错:', error); } }); } // 监听折叠/展开事件和DOM变化 function observeChanges() { const observer = new MutationObserver(mutations => { let needsProcessing = false; mutations.forEach(mutation => { // 检查属性变化 if (mutation.type === 'attributes' && mutation.attributeName === 'fold' && mutation.target.dataset.type === 'NodeListItem') { needsProcessing = true; } // 检查DOM结构变化 if (mutation.type === 'childList' && (mutation.target.dataset.type === 'NodeList' || mutation.target.closest('[data-type="NodeList"]'))) { needsProcessing = true; } }); if (needsProcessing) { processAllNodes(); } }); // 配置观察器 const config = { attributes: true, attributeFilter: ['fold'], childList: true, subtree: true }; // 启动观察器 observer.observe(document.body, config); } // 初始化函数 function initialize() { // 首次处理所有节点 processAllNodes(); // 开始监听变化 observeChanges(); // 每3秒强制重新检查一次,以防有漏掉的节点 setInterval(processAllNodes, 3000); } // 当DOM加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { initialize(); } })();

安装方法

设置 - 外观 - 代码片段(设置) - JS - 加号新建 - 粘贴代码 - 启用

PixPin20250309122821.png

代码由 Claude 编写,仅测试新建工作空间下单纯列表节点下的表现,但预计不会出现破坏性操作(仅操作 title 属性)

  • 思源笔记

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

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

    25213 引用 • 103964 回帖 • 1 关注
  • 代码片段

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

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

    147 引用 • 972 回帖

相关帖子

欢迎来到这里!

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

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