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

如何用JavaScript从JSON自动生成嵌套侧边栏导航菜单?

如何用JavaScript从JSON自动生成嵌套侧边栏导航?

我的需求说明

  • 目标是从JSON文件自动生成如下嵌套侧边栏导航结构:
  • 给定JSON数据示例:
[
  {"datasetID":"5fd4124900827","higherClassification":"Eukarya","kingdom":"Animalia","phylum":"Chordata","class":"Mammalia","order":"Artiodactyla","family":"Bovidae","genus":"Capra","subgenus":"None","vernacularName":"goat","commonName":"None","elementName":"Calcaneus","commonElementName":"None"},
  {"datasetID":"5fd4058e5c8d2","higherClassification":"Eukarya","kingdom":"Animalia","phylum":"Chordata","class":"Mammalia","order":"Artiodactyla","family":"Bovidae","genus":"Capra","subgenus":"None","vernacularName":"goat","commonName":"goat","elementName":"Femur","commonElementName":"None"}
]
  • 导航层级规则:class(顶层,如Mammalia)→order→family→genus→subgenus(仅当值不为"None"时加入)→vernacularName→elementName;同一vernacularName下重复的elementName需添加序号(如Calcaneus 1)。

我之前试过用PHP获取JSON再用JavaScript读取,但觉得document.write不是最优方案,想请教如何用JavaScript实现该需求?我有学习障碍,恳请耐心解答。


分步实现方案

第一步:把扁平JSON转换成树形结构

首先我们需要把平铺的JSON数组整理成符合导航层级的树形对象,这样后续生成DOM会更清晰。这里会处理两个核心逻辑:按指定层级构建节点、给重复的elementName加序号。

function buildNavigationTree(jsonData) {
  const root = {};
  // 定义导航的层级顺序,注意subgenus只有非None时才参与构建
  const hierarchy = ['class', 'order', 'family', 'genus', 'subgenus', 'vernacularName', 'elementName'];

  jsonData.forEach(item => {
    let currentNode = root;
    // 遍历每一层级
    hierarchy.forEach((level, index) => {
      // 跳过值为"None"的subgenus层级
      if (level === 'subgenus' && item[level] === 'None') {
        return;
      }
      let value = item[level];
      // 仅在最后一层(elementName)处理重复项
      if (level === 'elementName') {
        const parentNode = currentNode;
        let count = 1;
        // 统计当前父节点下同名elementName的数量
        Object.keys(parentNode).forEach(key => {
          if (key.startsWith(value)) {
            count++;
          }
        });
        // 如果存在重复,添加序号
        if (count > 1) {
          value = `${value} ${count}`;
        }
      }
      // 如果当前节点没有该子项,就创建新节点
      if (!currentNode[value]) {
        currentNode[value] = {};
      }
      // 移动到下一层节点
      currentNode = currentNode[value];
    });
  });

  return root;
}

第二步:递归生成侧边栏DOM元素

接下来用递归方式把树形结构转换成对应的<ul>和<li>元素,完全摒弃document.write,用原生DOM方法创建元素,更灵活且不会破坏页面结构。

function generateNavigationElement(tree) {
  // 创建最外层的ul,带sidebar-menu类
  const ul = document.createElement('ul');
  ul.className = 'sidebar-menu';

  // 递归生成子节点的内部函数
  function createNodes(parentElement, node) {
    // 遍历当前节点的所有子项
    Object.entries(node).forEach(([key, childNode]) => {
      const li = document.createElement('li');
      // 如果当前子项还有子节点,添加treeview类
      if (Object.keys(childNode).length > 0) {
        li.className = 'treeview';
      }
      // 设置节点文本
      li.textContent = key;

      // 如果有子节点,创建对应的treeview-menu ul并递归生成子元素
      if (Object.keys(childNode).length > 0) {
        const subUl = document.createElement('ul');
        subUl.className = 'treeview-menu';
        createNodes(subUl, childNode);
        li.appendChild(subUl);
      }

      // 将li添加到父元素中
      parentElement.appendChild(li);
    });
  }

  // 从根节点开始生成导航
  createNodes(ul, tree);
  return ul;
}

第三步:将导航挂载到页面上

最后把生成的导航元素添加到页面的指定容器中,比如页面上有一个<div id="sidebar-container"></div>:

// 初始化导航的异步函数
async function initNavigation() {
  try {
    // 替换成你的JSON文件路径
    const response = await fetch('your-data.json');
    const jsonData = await response.json();
    
    // 1. 构建树形结构
    const navTree = buildNavigationTree(jsonData);
    // 2. 生成DOM元素
    const navElement = generateNavigationElement(navTree);
    // 3. 挂载到页面容器
    const container = document.getElementById('sidebar-container');
    container.appendChild(navElement);
  } catch (error) {
    console.error('加载或处理导航数据失败:', error);
  }
}

// 页面加载完成后执行初始化
document.addEventListener('DOMContentLoaded', initNavigation);

针对学习障碍的额外解释

  1. 为什么不用document.write?
    document.write会直接写入文档流,页面加载完成后使用会覆盖整个页面,非常不灵活。而createElement和appendChild可以精确控制元素位置,不会破坏现有页面结构。

  2. 递归的作用是什么?
    导航是嵌套结构,层级不确定(比如subgenus可能存在或不存在),递归可以自动处理任意深度的嵌套,不用手动写多层循环。

  3. 重复elementName的处理逻辑
    在处理最后一层(elementName)时,我们会统计当前父节点(vernacularName对应的节点)下同名元素的数量,给新元素加上序号,比如第一个Calcaneus是"Calcaneus",第二个就是"Calcaneus 2"。

内容的提问来源于stack exchange,提问作者Spatial Digger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:06:37