基于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
相关产品推荐
相关产品推荐

