You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何遍历JSON子树以构建完整层级导航菜单?

解决菜单子树的完整层级路径遍历问题

首先先修正一下你提供的原始JSON结构,里面多了一层冗余的大括号,正确的格式应该是这样(避免后续解析出错):

[
  {
    "link": { "title": "Applications" },
    "has_children": true,
    "subtree": [
      {
        "link": { "title": "Projects" },
        "subtree": [
          { "link": { "title": "WORK/NEW YORK" } }
        ]
      }
    ]
  }
]

你遇到的问题很典型:嵌套循环只能处理固定层级的菜单,一旦菜单深度变化就失效;而之前尝试的递归因为和DOM操作绑定变得复杂。其实我们可以把路径收集和DOM操作完全分开,用递归或者迭代的方式专门处理路径遍历,这样就会简洁很多。

下面给你两种实用的方案:

方案一:递归遍历(专注路径收集,不碰DOM)

这个方法的核心是维护当前遍历的路径,遇到叶子节点(没有子节点)时,就把完整路径保存下来。完全不需要操作DOM,逻辑非常清晰:

const menuData = [
  {
    "link": { "title": "Applications" },
    "has_children": true,
    "subtree": [
      {
        "link": { "title": "Projects" },
        "subtree": [
          { "link": { "title": "WORK/NEW YORK" } }
        ]
      }
    ]
  }
];

function collectFullPaths(nodes, currentPath = [], result = []) {
  nodes.forEach(node => {
    // 将当前节点标题加入路径
    const updatedPath = [...currentPath, node.link.title];
    
    // 判断是否有子节点(用subtree是否存在且非空比has_children更可靠)
    if (node.subtree && node.subtree.length > 0) {
      // 递归处理子节点,传递更新后的路径
      collectFullPaths(node.subtree, updatedPath, result);
    } else {
      // 没有子节点,把完整路径拼接成你要的格式,加入结果数组
      result.push(updatedPath.join(' => '));
    }
  });
  return result;
}

// 调用示例
const allPaths = collectFullPaths(menuData);
console.log(allPaths); // 输出: ["Applications => Projects => WORK/NEW YORK"]

你可以拿到allPaths之后,再用它去做后续的DOM操作,这样逻辑拆分后,递归部分就完全不复杂了。

方案二:迭代式深度优先遍历(避免递归调用栈)

如果担心递归的调用栈问题(比如菜单层级极深),可以用栈来模拟递归过程,同样专注于路径收集:

function collectPathsIteratively(nodes) {
  const result = [];
  // 栈中存储的是 [当前要处理的节点数组, 当前已遍历的路径]
  const stack = [[nodes, []]];

  while (stack.length > 0) {
    const [currentNodes, currentPath] = stack.pop();
    
    currentNodes.forEach(node => {
      const updatedPath = [...currentPath, node.link.title];
      
      if (node.subtree && node.subtree.length > 0) {
        // 把子节点和更新后的路径压入栈
        stack.push([node.subtree, updatedPath]);
      } else {
        result.push(updatedPath.join(' => '));
      }
    });
  }
  
  // 因为栈是后进先出,最后反转结果保持原顺序
  return result.reverse();
}

// 调用示例
const iterativePaths = collectPathsIteratively(menuData);
console.log(iterativePaths); // 输出: ["Applications => Projects => WORK/NEW YORK"]

为什么这两种方案适合你?

  • 不管菜单有多少层级,都能自动适配,不像嵌套循环只能处理固定深度;
  • 完全把路径收集和DOM操作解耦,你可以先拿到所有完整路径,再去执行buildingMenuContent之类的DOM操作,逻辑更清晰;
  • 代码简洁,没有多余的DOM操作干扰,维护起来很方便。

内容的提问来源于stack exchange,提问作者Zyzz Shembesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:02:02