简体   繁体   English

如何从平面JavaScript数组创建分层HTML结构?

[英]How to create hierarchical HTML structure from flat JavaScript array?

I have been asked to create a stock list using a hierarchal system with parent ID's. 我被要求使用带有父ID的层次系统创建一个库存清单。 I'm having trouble displaying children under their parents. 我在带孩子到父母下时遇到麻烦。 I know I need to use a recursive function of some kind but my brain just won't let me work out how it would go together to accommodate for infinite amounts of indenting. 我知道我需要使用某种递归函数,但我的大脑不会让我知道如何将其结合起来以适应无限量的缩进。

Example JavaScript data... 示例JavaScript数据...

[
    {id: 1, parent_id: null, title: "Row 1"},
    {id: 2, parent_id: 1, title: "Row 2"},
    {id: 3, parent_id: 2, title: "Row 3"},
    {id: 4, parent_id: 2, title: "Row 4"}
]

Which the HTML should to look like... HTML应该是什么样的...

  • Row 1 第1行
    • Row 2 第2行
      • Row 3 第3行
      • Row 4 第4行

If anyone could help me it would be awesome as I've been stuck on this for almost 4 hours and I can't find anything that is relevant to my specific goal. 如果有人可以帮助我,那将是非常棒的事情,因为我已经坚持了将近4个小时,而我找不到与我的特定目标相关的任何东西。

Heads up : This requires your data to be ordered in a way that parent nodes appear before children reference them. 注意 :这要求您的数据必须按照父节点出现在子节点引用它们之前的顺序进行排序。 A sort could be done first, if required. 如果需要,可以先进行sort

Edit : a no-sort solution is posted below 编辑 :排序解决方案发布在下面

Here's a way to do it using Array.prototype.reduce . 这是使用Array.prototype.reduce的一种方法。

var arr = [
  {id: 1, parent_id: null, title: "Row 1"},
  {id: 2, parent_id: 1, title: "Row 2"},
  {id: 3, parent_id: 2, title: "Row 3"},
  {id: 4, parent_id: 2, title: "Row 4"}
];

var x = arr.reduce(function(map, node) {
  map.i[node.id] = node;
  node.children = [];
  node.parent_id === null ?
    map.result.push(node) :
    map.i[node.parent_id].children.push(node);
  return map;
}, {i:{}, result:[]}).result;

Explanation. 说明。 I'll step through the reduce process I used 我将逐步完成所用的减少过程

  1. initialize the reduce with {i:{}, result:[]} 使用{i:{}, result:[]} 初始化 reduce {i:{}, result:[]}

    We'll use the i object as a means of referencing parent nodes and the result array to store top-level root nodes 我们将使用i对象作为引用父节点和result数组的方法,以存储顶级根节点

  2. index each node by id using map.i[node.id] = node 使用map.i[node.id] = nodeid 索引每个map.i[node.id] = node

  3. If the node is a root node ( parent_id === null ), add it to the result with map.result.push(node) 如果该节点是根节点parent_id === null ),则使用map.result.push(node)将其添加到结果中

  4. If the node is a child node ( parent_id !== null ), add it to the children array of the parent node with map.index[node.parent_id].children.push(node) 如果该节点是子节点parent_id !== null ),则使用map.index[node.parent_id].children.push(node)将其添加到父节点的子节点数组中。


Okay, let's check if it worked 好吧,让我们检查一下是否可行

// all root nodes
// see output below
console.log(JSON.stringify(x, null, "  "));

// first "root" node
console.log(x[0].id); //=> 1

// first child of first root node
console.log(x[0].children[0].id); //=> 2

// first child of first child of first root node
console.log(x[0].children[0].children[0].id); //=> 3

// second child of first child of first root node
console.log(x[0].children[0].children[1].id); //=> 4

All root nodes output 所有根节点输出

[
  {
    "id": 1,
    "parent_id": null,
    "title": "Row 1",
    "children": [
      {
        "id": 2,
        "parent_id": 1,
        "title": "Row 2",
        "children": [
          {
            "id": 3,
            "parent_id": 2,
            "title": "Row 3",
            "children": []
          },
          {
            "id": 4,
            "parent_id": 2,
            "title": "Row 4",
            "children": []
          }
        ]
      }
    ]
  }
]

If your initial data is unsorted ... 如果您的初始数据未排序 ...

The reduce method is a little more difficult in this case. 在这种情况下, reduce方法要困难一些。 Admittedly, pretty much all elegance is lost with this solution, but I've provided it to show it's still possible. 诚然,此解决方案几乎失去了所有的优雅,但是我提供了它来证明它仍然可行。

// this works on arbitrarily sorted data
var x = arr.reduce(function(map, node) {
  map.i[node.id] = node;
  node.children = [];
  if (node.parent_id === null) {
    map.result.push(node);
  }
  else if (node.parent_id in map.i) {
    map.i[node.parent_id].children.push(node);
  }
  else {
    (node.parent_id in map.cache) ?
      map.cache[node.parent_id].push(node) :
      map.cache[node.parent_id] = [node];
  }
  if (node.id in map.cache) {
    node.children = node.children.concat(map.cache[node.id]);
    delete map.cache[node.id];
  }
  return map;
}, {i:{}, cache:{}, result:[]}).result;

You use an object that keeps track of your id's. 您使用一个跟踪ID的对象。 For example: 例如:

var idObj = {};
var root = null;

// first populate the object with elements
for (var i = 0; i < arr.length; i++) {
  var item = arr[i];
  idObj[item.id] = item;
}

// then attach the children to the parents
for (var i = 0; i < arr.length; i++) {
  var item = arr[i];
  var parent = idObj[item.parent_id];
  if (parent) {
    parent.children = parent.children || [];
    parent.children.push(item);
  } else if (item.parent_id === null) { 
    //set the item as root if it has no parents
    root = item;
  }
}

This will add a children property to all those items. 这将为所有这些项目添加一个children属性。

Note: this solution is not recursive, but you can traverse the tree recursively starting from the root variable. 注意:此解决方案不是递归的,但是您可以从root变量开始递归地遍历树。

Inspired by Naomik, the code will fail when the parent_id s aren't in the correct position. 受Naomik的启发,当parent_id不在正确位置时,代码将失败。 Added a sorting function that will set them in the correct order. 添加了排序功能,可以按正确的顺序设置它们。

 obj = [ {id: 2, parent_id: 1, title: "Row 2"}, {id: 3, parent_id: 2, title: "Row 3"}, {id: 4, parent_id: 2, title: "Row 4"}, {id: 1, parent_id: null, title: "Row 1"} ] obj.sort(function(a, b){ return (a.parent_id == null ? 0 : a.parent_id) - (b.parent_id == null ? 0 : b.parent_id); }); var tree = document.getElementById("tree"); for (var i = 0; i < obj.length; ++i) { if (obj[i].parent_id == null) { createTreeElement("li", obj[i].id, obj[i].title, tree); } else { var treeChildNode = document.getElementById("t" + obj[i].parent_id).getElementsByTagName("ul"); if (treeChildNode.length) { createTreeElement("li", obj[i].id, obj[i].title, treeChildNode[0]); } else { createTreeElement("ul", obj[i].parentId, "", document.getElementById("t" + obj[i].parent_id)); createTreeElement("li", obj[i].id, obj[i].title, document.getElementById("t" + obj[i].parent_id).getElementsByTagName("ul")[0]); } } } function createTreeElement(name, id, text, parent) { var node = document.createElement(name); node.id = "t" + id; node.innerHTML = text; parent.appendChild(node); } 
 <ul id="tree"> </ul> 

This code is just a prove of concept in HTML to @Daniel Weiners answer why recursion isn't needed here based upon the object model. 这段代码只是@Daniel Weiners回答HTML的概念验证,为什么这里不需要基于对象模型进行递归。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM