如何用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);
针对学习障碍的额外解释
为什么不用document.write?
document.write会直接写入文档流,页面加载完成后使用会覆盖整个页面,非常不灵活。而createElement和appendChild可以精确控制元素位置,不会破坏现有页面结构。递归的作用是什么?
导航是嵌套结构,层级不确定(比如subgenus可能存在或不存在),递归可以自动处理任意深度的嵌套,不用手动写多层循环。重复elementName的处理逻辑
在处理最后一层(elementName)时,我们会统计当前父节点(vernacularName对应的节点)下同名元素的数量,给新元素加上序号,比如第一个Calcaneus是"Calcaneus",第二个就是"Calcaneus 2"。
内容的提问来源于stack exchange,提问作者Spatial Digger
相关产品推荐
相关产品推荐

