swelet
swelet

Reputation: 8702

Render out a tree

I need to render out flat data with relations into an html tree structure in javascript. I need to do it without help from any libraries. The data itself is flat:

[
{id : 1, title : one, parent : null},
{id : 2, title : two, parent : 1},
{id : 3, title : three, parent : 2},
{id : 4, title : four, parent : null},
{id : 5, title : five, parent : 4},
{id : 6, title : six, parent : 4},
{id : 7, title : seven, parent : 6},
{id : 8, title : eight, parent : 7},
{id : 9, title : nine, parent : 8}
]

And this is the desired result:

<div>one
  <div>two
    <div>three</div>
  </div>
</div>
<div>four
  <div>five</div>
  <div>six
    <div>seven
      <div>eight
        <div>nine
        </div>
      </div>
    </div>
  </div>
</div>

The depth of the data is arbitrary (i.e. it can be one or a hundred levels deep depending on the data).

I've been looking around for an answer, but most of them point to libraries or use data that itself is nested (which in most cases only would push the problem of nesting over to the server).

Upvotes: 0

Views: 75

Answers (2)

Oriol
Oriol

Reputation: 288140

An iterative way with cost O(n) would be

function makeTree(data) {
  var elements = Array(data.length + 1),
      proto = document.createElement('div');
  for(var i=0; i<=data.length; ++i)
    elements[i] = proto.cloneNode(false);
  for(var i=0; i<data.length; ++i) {
    var elem = elements[data[i].id];
    elements[+data[i].parent].appendChild(elem);
    elem.insertBefore(
      document.createTextNode(data[i].title),
      elem.firstChild
    );
  }
  return elements[0];
}

var data = [
  {id : 9, title : "nine", parent : 8},
  {id : 1, title : "one", parent : null},
  {id : 2, title : "two", parent : 1},
  {id : 3, title : "three", parent : 2},
  {id : 4, title : "four", parent : null},
  {id : 5, title : "five", parent : 4},
  {id : 6, title : "six", parent : 4},
  {id : 7, title : "seven", parent : 6},
  {id : 8, title : "eight", parent : 7}
];
function makeTree(data) {
  var elements = Array(data.length + 1),
      proto = document.createElement('div');
  for(var i=0; i<=data.length; ++i)
    elements[i] = proto.cloneNode(false);
  for(var i=0; i<data.length; ++i) {
    var elem = elements[data[i].id];
    elements[+data[i].parent].appendChild(elem);
    elem.insertBefore(
      document.createTextNode(data[i].title),
      elem.firstChild
    );
  }
  return elements[0];
}
document.body.appendChild(makeTree(data));
div {
  padding-left: 10px;
}

The code above will work even if you reference an element as a parent before defining its data. However, it assumes the IDs are consecutive integers from 1 to n. If they can be sparse or strings, then use an object instead of an array. In average it will also be O(n).

var data = [
  {id : "id9", title : "nine", parent : "id8"},
  {id : "id1", title : "one", parent : null},
  {id : "id2", title : "two", parent : "id1"},
  {id : "id3", title : "three", parent : "id2"},
  {id : "id4", title : "four", parent : null},
  {id : "id5", title : "five", parent : "id4"},
  {id : "id6", title : "six", parent : "id4"},
  {id : "id7", title : "seven", parent : "id6"},
  {id : "id8", title : "eight", parent : "id7"}
];
function makeTree(data) {
  function getEl(id) {
    if(id === null) return wrapper;
    return elements[id] = elements[id] || wrapper.cloneNode(false);
  }
  var elements = {},
      wrapper = document.createElement('div');
  for(var i=0; i<data.length; ++i) {
    var el = getEl(data[i].id);
    getEl(data[i].parent).appendChild(el);
    el.insertBefore(
      document.createTextNode(data[i].title),
      el.firstChild
    );
  }
  return wrapper;
}
document.body.appendChild(makeTree(data));
div {
  padding-left: 10px;
}

Upvotes: 1

trincot
trincot

Reputation: 350270

Here is a recursive function to turn an array of objects (with your structure) into an html string, representing a hierarchy of div tags.

function menuTree(nodes, parentId) {
    return nodes.reduce(function(html, node) {
        return html + (node.parent === parentId
            ? '\n<div>' + node.title + menuTree(nodes, node.id) + '</div>'
            : '');
    }, '');
}

You call it like this:

var nodes = [
    {"id": 1, "title": "one",   "parent": null},
    {"id": 2, "title": "two",   "parent": 1},
    {"id": 3, "title": "three", "parent": 2},
    {"id": 4, "title": "four",  "parent": null},
    {"id": 5, "title": "five",  "parent": 4},
    {"id": 6, "title": "six",   "parent": 4},
    {"id": 7, "title": "seven", "parent": 6},
    {"id": 8, "title": "eight", "parent": 7},
    {"id": 9, "title": "nine",  "parent": 8}
];
var html = menuTree(nodes, null);
console.log(html);

Here is a fiddle that demonstrates the above interactively. This fiddle allows you to change the json description and reproduce the div tree accordingly. By using some basic css the div tree is rendered with indentation.

Upvotes: 1

Related Questions