1

I need to create html for treeview from array of unspecified number of nodes. Here is an example

var array = [
    {
        Description: "G",
        Id: 1,
        guid: "c8e63b35",
        parent: null,
        Children: [
            {
                Description: "Z",
                Id: 9,
                guid: "b1113b35",
                parent: "c8e63b35",
                Children: [
                    {
                        Description: "F",
                        Id: 3,
                        guid: "d2cc2233",
                        parent: "b1113b35",
                    }
                ]
            }, 
        ]
    },
    {
        Description: "L",
        Id: 2,
        guid: "a24a3b1a",
        parent: null,
        Children: [
        {
            Description: "K",
            Id: 4,
            guid: "cd3b11caa",
            parent: "a24a3b1a",
        }
    }
]

the result should be

<ul>
    <li id="1" data-guid="c8e63b35">G
        <ul>
            <li id="9" data-guid="b1113b35">Z
                <ul>
                    <li id="3" data-guid="d2cc2233">F
                    </li>
                </ul>
            </li>
        </ul>
    </li>
    <li id="2" data-guid="a24a3b1a">L
        <ul>
            <li id="4" data-guid="cd3b11caa">K
            </li>
        </ul>
    </li>
</ul>

I wrote recursion function which generate html correctly in this example but in other cases it works perfectly to 197 depth only. If nested nodes are more than 197 it thrown an exception

"The maximum call stack size"

Is there a way to do this without using recursive functions in JavaScript?

EDIT: Here is my recursion function

    var _generateHTML = function (array, html) {
        for (var i = 0; i < array.length; i++) {
            html += "<li id=\"" + array[i].Id + "\" data-guid=\"" + array[i].guid + "\">" + array[i].Description +
                        "<ul>" + _generateHTML(array[i].Children, "") + "</ul>" +
                    "</li>";
        }
        return html;
    }

I cannot use external libraries because this is for my work. I created this tree using recursive functions earlier. I am wondering if this is possible thats all.

Adam Mrozek
  • 1,410
  • 4
  • 26
  • 49
  • hey again! share your recursive solution so we can build up from there easily – franciscod Jul 15 '15 at 07:39
  • the html-generating code should run in the browser and generate the HTML in realtime? or should the HTML be generated in the server? on which platform? – franciscod Jul 15 '15 at 07:43
  • I corrected my answer. Entire HTML should be generated as string and passed to $(selector").html() function – Adam Mrozek Jul 15 '15 at 07:55
  • so you want to put recursive input into a non recursive function? how does that make sense? well.. you could create this in various recursive ways.. both ressource saving and ressource intensive. – GottZ Jul 15 '15 at 07:56
  • Which browser do you use? According to sources chrome will let you do above 10000 calls it becomes a problem http://www.2ality.com/2014/04/call-stack-size.html – Emil Ingerslev Jul 15 '15 at 07:59
  • @GottZ It does make sense. Here is solution to create object in that way http://stackoverflow.com/questions/31383869/converting-flat-structure-to-hierarchical now, I need to create html from it. This solution is efficient and easy without using any recursive functions. – Adam Mrozek Jul 15 '15 at 07:59
  • @AdamMrozek the input you provided in THIS question is recursive... to be honest.. you can use the given answer to your previous question to generate the html aswell. evaluating all your given information i think this question makes no sense since you already have all the required code to create it. – GottZ Jul 15 '15 at 08:03
  • @EmilIngerslev I use newest chrome, windows 7 x64. I don't know why I have this problem. – Adam Mrozek Jul 15 '15 at 08:03
  • @GottZ Ok i know that, but I've got no idea how to do this correctly... The html piece should be injected into existing html on specified index. This is not so obvious for me. – Adam Mrozek Jul 15 '15 at 08:06
  • @AdamMrozek are you using this in a browser or in a backend to generate html? – GottZ Jul 15 '15 at 08:06
  • @GottZ I am using this in browser. – Adam Mrozek Jul 15 '15 at 08:09
  • @AdamMrozek i'll see what i can do. i'll use the non recursive input to generate the html non recursively. – GottZ Jul 15 '15 at 08:11
  • Just tested you code out in chrome with `function build(i) { if(i) { return { Children: [build(i-1)] } } else { return {Children: [] }} }; _generateHTML([build(10000)],"");` and it did not give any errors. Can you supply steps to reproduce? Maybe in a running example? – Emil Ingerslev Jul 15 '15 at 08:12
  • Question is about solving this in non-recursive way. Target solution should handle stack exceed. – Adam Mrozek Jul 15 '15 at 08:19
  • @GottZ Hmm, I think that this solution can be an accepted answer. Can you explain how to do it? – Adam Mrozek Jul 15 '15 at 08:20
  • @AdamMrozek even though i'd prefer franciscod's solution due to speed reasons, here is mine: http://stackoverflow.com/a/31426683/1519836 it uses pure dom operations instead of gluing all the html together. – GottZ Jul 15 '15 at 12:52

3 Answers3

3

This does the trick (edit: also does indentation):

function indent (num) {
  var INDENT_SIZE = 4
  return new Array(INDENT_SIZE * num + 1).join(" ");
}


function ulli(input) {
    var CLOSE_IT = ['JUST PLEASE DO IT']
    var queue = []

    var output = ""
    var depth = 0;

    queue = queue.concat(input)
    output += "<ul>\n"
    depth++

    while (queue.length > 0) {
        var node = queue.shift()

        if (node == CLOSE_IT) {
            depth--

            output += indent(depth)
            output += "</ul></li>\n"
            continue
        }

        output += indent(depth)
        output += '<li id="' + node.Id + '" data-guid="' + node.guid + '">' + node.Description;

        if (node.Children) {
            depth++
            output += "<ul>\n"
            newQueue = [].concat(node.Children)
            newQueue.push(CLOSE_IT)
            queue = newQueue.concat(queue)
        } else {
            output += "</li>\n"
        }

    }

    output += "</ul>"

    return output
}
franciscod
  • 992
  • 4
  • 17
0

Build a queue and add your root elements in it, do a while on the queue and add every child to queue. For putting elements in the correct position, u need to find their parent in dom and then add them to it.

alizelzele
  • 892
  • 2
  • 19
  • 34
0

just for the sake of completeness i'm going to provide my solution aswell.
i just want you to know that you can do DOM manipulation to achieve this.

except one small thing that i'm going to try to optimize i like the solution of @franciscod alot.
if my guess is correct i'll just edit his answer.

even though this might not be the fastest solution, this way you are also able to register events to each node right away.

example of it running: http://codepen.io/GottZ/pen/jPKpaP

this includes your raw input from this question: Converting flat structure to hierarchical
as mentioned in this comment: generate html of inifnite depth menu without recursion

in my opinion you should not use an id for every element but thats up to you to decide.

code:

var content = document.getElementById('content');

var flatArray = [
    {
        Description: 'G',
        Id: 1,
        guid: 'c8e63b35',
        parent: null
    },
    {
        Description: 'Z',
        Id: 9,
        guid: 'b1113b35',
        parent: 'c8e63b35'
    },
    {
        Description: 'F',
        Id: 3,
        guid: 'd2cc2233',
        parent: 'b1113b35'
    },
    {
        Description: 'L',
        Id: 2,
        guid: 'a24a3b1a',
        parent: null
    },
    {
        Description: 'K',
        Id: 4,
        guid: 'cd3b11caa',
        parent: 'a24a3b1a'
    }
];

var container = document.createElement('ul');
var allNodes = {};

flatArray.forEach(function (v) {
    var element = document.createElement('li');

    allNodes[v.guid] = {
        element: element
    };

    element.setAttribute('id', v.Id);
    element.setAttribute('data-guid', v.guid);
    element.appendChild(document.createTextNode(v.Description));

    if (!v.parent) {
        container.appendChild(element);
        return;
    }

    var p = allNodes[v.parent];
    if (!p.ul) {
        p.ul = p.element.appendChild(document.createElement('ul'));
    }

    p.ul.appendChild(element);
});

content.appendChild(container);
Community
  • 1
  • 1
GottZ
  • 4,824
  • 1
  • 36
  • 46