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

基于parent_id构建可点击展开子节点的HTML树形视图实现方案

可点击展开树形结构实现方案

步骤1:平级数据转嵌套树形结构

你的原始数据是带parent_id的平级列表,第一步需要先处理为层级嵌套结构,每个节点新增children字段存储对应子节点,以下是JS实现代码:

// 原始数据(将Elixir结构转换为JS可处理的对象数组)
const originData = [
  {name: "name1", id: 1, parent_id: null},
  {name: "name2", id: 2, parent_id: 1},
  {name: "name3", id: 3, parent_id: 2}
]

function buildTree(data) {
  const result = []
  const nodeMap = new Map()
  // 先将所有节点存入映射表,同时初始化children字段
  data.forEach(item => {
    nodeMap.set(item.id, {...item, children: []})
  })
  // 遍历将子节点挂载到对应父节点的children中
  data.forEach(item => {
    const parentId = item.parent_id
    if (parentId === null) {
      result.push(nodeMap.get(item.id))
    } else {
      const parentNode = nodeMap.get(parentId)
      parentNode?.children.push(nodeMap.get(item.id))
    }
  })
  return result
}

const treeData = buildTree(originData)

步骤2:编写基础HTML结构与样式

核心逻辑是每个节点包含「头部(箭头+节点名)」和「子节点容器」两部分,通过内边距实现层级缩进,通过类名控制展开/收起状态:

<style>
.tree-node {
  margin: 4px 0;
}
.node-header {
  display: flex;
  align-items: center;
  cursor: pointer;
  user-select: none;
}
.arrow {
  display: inline-block;
  width: 16px;
  transition: transform 0.2s;
}
/* 展开状态箭头旋转90度变为向下 */
.arrow.expand {
  transform: rotate(90deg);
}
/* 无子节点的节点隐藏箭头 */
.arrow.empty {
  opacity: 0;
  pointer-events: none;
}
.node-name {
  margin-left: 4px;
}
.children-container {
  padding-left: 20px;
  display: none;
}
.children-container.expand {
  display: block;
}
</style>
<div id="treeContainer"></div>

步骤3:递归渲染节点+绑定交互事件

通过递归函数渲染嵌套树形结构,同时给有子节点的头部绑定点击事件,切换展开收起状态:

function renderTree(tree, parentContainer) {
  tree.forEach(node => {
    // 生成节点外层容器
    const nodeEl = document.createElement('div')
    nodeEl.className = 'tree-node'

    // 生成节点头部
    const header = document.createElement('div')
    header.className = 'node-header'
    const arrow = document.createElement('span')
    arrow.className = 'arrow'
    arrow.textContent = '→'
    if (node.children.length === 0) arrow.classList.add('empty')
    const name = document.createElement('span')
    name.className = 'node-name'
    name.textContent = node.name
    header.append(arrow, name)
    nodeEl.append(header)

    // 渲染子节点
    if (node.children.length > 0) {
      const childrenContainer = document.createElement('div')
      childrenContainer.className = 'children-container'
      renderTree(node.children, childrenContainer)
      nodeEl.append(childrenContainer)
      // 绑定点击展开/收起事件
      header.addEventListener('click', () => {
        arrow.classList.toggle('expand')
        childrenContainer.classList.toggle('expand')
      })
    }

    parentContainer.append(nodeEl)
  })
}

// 把树形结构渲染到页面
const container = document.getElementById('treeContainer')
renderTree(treeData, container)

如果需要默认展开根节点,渲染完成后手动触发一次根节点头部的点击事件即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 03:39:02