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

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

功能说明

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

如图

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 属性)

  • 思源笔记

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

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

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

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

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

    285 引用 • 1986 回帖

相关帖子

欢迎来到这里!

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

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