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

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

解决分类学JSON生成嵌套侧边栏导航的问题

Hey there! Let's work through this together nice and slowly—no stress, I’ll break every part down so it’s easy to follow. You’re right that document.write isn’t a good approach (it can overwrite your whole page or cause weird timing issues), so we’ll build this properly using DOM-friendly methods, adapting your existing code.

核心思路

First, we need to do two key things:

  1. Transform the flat JSON data into a nested hierarchy matching your required order: class → order → family → genus → subgenus (if not "None") → vernacularName → elementName (with number suffixes for duplicate element names).
  2. Convert that hierarchy into the nested <ul>/<li> structure you need, using the correct CSS classes (sidebar-menu, treeview, treeview-menu).

Step 1: Set Up Your HTML Container

First, add a container to your page where the sidebar will live—this replaces the need for document.write:

<div id="sidebar-container"></div>

Step 2: Full JavaScript Implementation (Adapted for Your Code)

We’ll use your existing PHP/JS setup, either pulling data via AJAX or using the pre-fetched PHP data (I’ll show both options).

Option 1: Use Pre-Fetched PHP Data (Faster, No AJAX)

Since you already have $data from PHP, we can pass it directly to JavaScript without another AJAX call:

// Get the pre-fetched data from PHP
const jsonData = <?php echo json_encode($data); ?>;
const version = "<?php echo $version; ?>";

// 1. Build the nested hierarchy from the JSON data
const sidebarHierarchy = {};

jsonData.forEach(item => {
    // Define the exact hierarchy order, skipping subgenus if it's "None"
    const hierarchyLevels = [
        item.class,
        item.order,
        item.family,
        item.genus,
        ...(item.subgenus !== "None" ? [item.subgenus] : []), // Only include subgenus if it's not "None"
        item.vernacularName,
        item.elementName
    ];

    // Traverse the hierarchy to add each node
    let currentNode = sidebarHierarchy;
    hierarchyLevels.forEach((levelName, index) => {
        // Handle duplicate element names (last level only)
        if (index === hierarchyLevels.length - 1 && currentNode[levelName]) {
            let suffix = 2;
            let uniqueName = `${levelName} ${suffix}`;
            // Keep incrementing until we find an unused name
            while (currentNode[uniqueName]) {
                suffix++;
                uniqueName = `${levelName} ${suffix}`;
            }
            levelName = uniqueName;
        }

        // Create the node if it doesn't exist
        if (!currentNode[levelName]) {
            currentNode[levelName] = {
                children: {},
                isLeaf: index === hierarchyLevels.length - 1 // Mark if this is the final element
            };
        }

        // Move to the next level of the hierarchy
        currentNode = currentNode[levelName].children;
    });
});

// 2. Recursive function to generate the nested HTML menu
function buildSidebarHTML(node, isTopLevel = false) {
    // Top level uses "sidebar-menu", submenus use "treeview-menu"
    let menuHTML = isTopLevel ? '<ul class="sidebar-menu">' : '<ul class="treeview-menu">';

    // Loop through each node in the current hierarchy level
    Object.entries(node).forEach(([nodeName, nodeData]) => {
        // Add "treeview" class if the node has children
        const liClass = Object.keys(nodeData.children).length > 0 ? 'class="treeview"' : '';
        menuHTML += `<li ${liClass}>${nodeName}`;

        // If there are children, recursively build the submenu
        if (Object.keys(nodeData.children).length > 0) {
            menuHTML += buildSidebarHTML(nodeData.children);
        }

        menuHTML += '</li>';
    });

    menuHTML += '</ul>';
    return menuHTML;
}

// 3. Generate the full sidebar HTML and insert it into the container
const finalSidebarHTML = buildSidebarHTML(sidebarHierarchy, true);
$('#sidebar-container').html(finalSidebarHTML);

Option 2: Use AJAX (If You Need to Fetch Data Separately)

If you still want to use $.getJSON like your original code, here’s the adapted version:

const version = "<?php echo $version; ?>";

$.getJSON(`bonify${version}/app/json/data2.json`, function(jsonData) {
    // 1. Build the nested hierarchy (same code as Option 1)
    const sidebarHierarchy = {};

    jsonData.forEach(item => {
        const hierarchyLevels = [
            item.class,
            item.order,
            item.family,
            item.genus,
            ...(item.subgenus !== "None" ? [item.subgenus] : []),
            item.vernacularName,
            item.elementName
        ];

        let currentNode = sidebarHierarchy;
        hierarchyLevels.forEach((levelName, index) => {
            if (index === hierarchyLevels.length - 1 && currentNode[levelName]) {
                let suffix = 2;
                let uniqueName = `${levelName} ${suffix}`;
                while (currentNode[uniqueName]) {
                    suffix++;
                    uniqueName = `${levelName} ${suffix}`;
                }
                levelName = uniqueName;
            }

            if (!currentNode[levelName]) {
                currentNode[levelName] = { children: {}, isLeaf: index === hierarchyLevels.length - 1 };
            }

            currentNode = currentNode[levelName].children;
        });
    });

    // 2. Build HTML (same as Option 1)
    function buildSidebarHTML(node, isTopLevel = false) {
        let menuHTML = isTopLevel ? '<ul class="sidebar-menu">' : '<ul class="treeview-menu">';

        Object.entries(node).forEach(([nodeName, nodeData]) => {
            const liClass = Object.keys(nodeData.children).length > 0 ? 'class="treeview"' : '';
            menuHTML += `<li ${liClass}>${nodeName}`;

            if (Object.keys(nodeData.children).length > 0) {
                menuHTML += buildSidebarHTML(nodeData.children);
            }

            menuHTML += '</li>';
        });

        menuHTML += '</ul>';
        return menuHTML;
    }

    // 3. Insert into page
    const finalSidebarHTML = buildSidebarHTML(sidebarHierarchy, true);
    $('#sidebar-container').html(finalSidebarHTML);
});

What This Does (Breakdown for Learning)

Let’s go over the key parts so you understand:

  1. Hierarchy Building: We loop through each JSON item and build a nested object where each level maps to your required taxonomy order. For duplicate element names, we add a number suffix (like "Calcaneus 2") to avoid duplicates.
  2. Recursive HTML Generation: The buildSidebarHTML function calls itself to create submenus. It adds the correct CSS classes:
    • Top-level <ul> uses sidebar-menu
    • Parent <li> elements with children get the treeview class
    • Submenus use treeview-menu
  3. DOM Insertion: Instead of document.write, we use $('#sidebar-container').html(...) to safely add the menu to your page without breaking existing content.

Testing with Your Sample Data

Using your sample JSON, this will generate exactly the structure you want:

<ul class="sidebar-menu">
  <li class="treeview">Mammalia
    <ul class="treeview-menu">
      <li class="treeview">Artiodactyla
        <ul class="treeview-menu">
          <li class="treeview">Bovidae
            <ul class="treeview-menu">
              <li class="treeview">Capra
                <ul class="treeview-menu">
                  <li class="treeview">goat
                    <ul class="treeview-menu">
                      <li>Calcaneus</li>
                      <li>Femur</li>
                    </ul>
                  </li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

内容的提问来源于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:55