HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Creating lists and sublists dynamically

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
sublistsdynamicallycreatinglistsand

Problem

I'm working on a simple little one page website for giving a presentation and I have some working code now, but I'm thinking there has to be a more elegant way of doing this. I would describe myself, at best, as a "dabbler" in JavaScript at this point; I'm much more comfortable with statically-typed languages and am doing this as a learning exercise.

Here's my JSFiddle showing what I am doing.

In my "real" site, the PageContent object is coming from an AJAX POST response (via an instance of node.js). I've included all of that code to make it easier to understand what is going on (any suggestions on how to restructure that server-side code would also be appreciated). If I have the time, I'll probably read these "points" from a config file or something else, but for now, a switch statement fits my needs.

The segment of this that makes me the most uneasy is this:

$('#pageContent').append('' + mp.main);
    if (mp.subPoints.length > 0){
        $('li#'+i).append('');
        $.each(mp.subPoints, function(index, sp){
            $('li#'+i+' ul').append('' + sp + '');
        });
        $('li#'+i).append('');
    }
    $('#pageContent').append('');


I don't like how I am having to add id values to my list items so I can then append my subpoints to the correct parent. Is there a less hacky-feeling way to go about this?

In case it isn't obvious from the fiddle, the page should show a list of main points, and, if they exist, subpoints under each main point. I'm basically trying to mimic PowerPoint.

Any criticisms of this code would be gladly accepted; I know this probably isn't idiomatic JavaScript.

Solution

This will depend on your content and how you prepare it, but I'd suggest a very generic solution that'll work for any level of nested points. Now, arbitrary nesting doesn't appear to be required in your case, but, hey, nice to have.

Suppose your JSON content is structured like so:

var points = [
    {title: "Point", children: [
        {title: "Point"},
        {title: "Point"},
        {title: "Point"},
        {title: "Point", children: [
            {title: "Point"},
            {title: "Point"},
            {title: "Point", children: [
              // more...?
            ]}
        ]}
    ]}
]


You'll note that you can just keep nesting the points indefinitely.

To render this as HTML, you can use a recursive function. Like this:

function buildList(parentElement, items) {
    var i, l, list, li;
    if( !items || !items.length ) { return; } // return here if there are no items to render
    list = $("").appendTo(parentElement); // create a list element within the parent element
    for(i = 0, l = items.length ; i ").text(items[i].title);  // make a list item element
        buildList(li, items[i].children);          // add its subpoints
        list.append(li);
    }
}


And call it like so:

buildList($("#pageContent").empty(), points);


The point is that since the structure is recursive, it can nest to any depth, but the code is simpler.

Here's a jsfiddle. This is quite a different approach, and Daniel Cook's answer is probably more immediately applicable to you current code, but I thought it worth to point out.

You could also extend it to add the "1", "1.1", "1.2" (and so on) numbers to the titles.

Code Snippets

var points = [
    {title: "Point", children: [
        {title: "Point"},
        {title: "Point"},
        {title: "Point"},
        {title: "Point", children: [
            {title: "Point"},
            {title: "Point"},
            {title: "Point", children: [
              // more...?
            ]}
        ]}
    ]}
]
function buildList(parentElement, items) {
    var i, l, list, li;
    if( !items || !items.length ) { return; } // return here if there are no items to render
    list = $("<ul></ul>").appendTo(parentElement); // create a list element within the parent element
    for(i = 0, l = items.length ; i < l ; i++) {
        li = $("<li></li>").text(items[i].title);  // make a list item element
        buildList(li, items[i].children);          // add its subpoints
        list.append(li);
    }
}
buildList($("#pageContent").empty(), points);

Context

StackExchange Code Review Q#34002, answer score: 3

Revisions (0)

No revisions yet.