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

时间:2015-01-19 21:05:45

标签: javascript html hierarchy

我被要求使用具有父ID的层级系统创建库存清单。我在父母身边展示孩子时遇到了麻烦。我知道我需要使用某种类型的递归函数,但是我的大脑不会让我知道如何将它们组合起来以适应无限量的缩进。

示例 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"}
]

HTML 应该看起来像......

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

如果有人能帮助我,那将是非常棒的,因为我已经坚持了将近4个小时,我找不到任何与我的具体目标相关的东西。

3 个答案:

答案 0 :(得分:2)

  

抬头:这要求您的数据以父节点出现在子节点引用之前的方式进行排序。如果需要,可以先sort完成。

     

修改:在

下面发布了无排序解决方案

以下是使用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;

解释。我将逐步完成我使用的简化过程

  1. 使用{i:{}, result:[]}

    初始化缩小

    我们将使用i对象作为引用父节点的方法,使用result数组来存储顶级根节点

  2. 索引每个节点id使用map.i[node.id] = node

  3. 如果节点是根节点parent_id === null),请使用map.result.push(node)

  4. 将其添加到结果中
  5. 如果节点是子节点parent_id !== null),请将其添加到父节点的子数组map.index[node.parent_id].children.push(node)


  6. 好的,让我们检查一下是否有效

    // 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
    

    所有根节点输出

    [
      {
        "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": []
              }
            ]
          }
        ]
      }
    ]
    

      

    如果您的初始数据未排序 ...

    在这种情况下,reduce方法有点困难。不可否认,这个解决方案几乎失去了所有的优雅,但我已经提供它以表明它仍然可能。

    // 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;
    

答案 1 :(得分:0)

您使用一个跟踪您的身份的对象。 例如:

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;
  }
}

这将为所有这些项添加children属性。

注意:此解决方案递归,但您可以从root变量开始递归遍历树。

答案 2 :(得分:0)

受Naomik的启发,当parent_id未处于正确位置时,代码将失败。添加了一个排序功能,可以按正确的顺序设置它们。

&#13;
&#13;
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);
}
&#13;
<ul id="tree">
  
</ul>
&#13;
&#13;
&#13;

这段代码只是对HTML中概念的证明@Daniel Weiners回答了为什么基于对象模型不需要递归。