简体   繁体   中英

Creating a Nested List with an Object Array

I have a set of SQL data which will change frequently and I need to create a nested, unordered list from that on a regular basis. I have converted the SQL data into an object array, but I'm stuck on the logic for creating the unordered list from that.

I tried making a recursive function, but I'm not experienced enough with those, so

The data contains these fields:

ID  Category Name   ParentID

So, each category has an ID and a ParentID (which matches the ID of another category). Root categories all have the same dummy ParentID.

The javascript object has the same properties. Here's an example:

var Categories = [
new Category(1, 'Root', 0),
new Category(2, 'Cat1', 1),
new Category(3, 'Cat2', 2),
new Category(4, 'Cat3', 5),
new Category(5, 'Cat4', 1),
new Category(6, 'Cat5', 5),
new Category(7, 'Cat6', 5),
new Category(8, 'Cat7', 1),
new Category(9, 'Cat8', 2),
new Category(10, 'Cat9', 1),
new Category(11, 'Cat10', 10),
new Category(12, 'Cat11', 1),
new Category(13, 'Cat12', 8)
]

I need to use that object array to make an unordered list that would look like this:

<ul>
<li>Cat1
    <ul>
        <li>Cat2</li>
        <li>Cat8</li>
    </ul>

<li>Cat4
    <ul>
        <li>Cat3</li>
        <li>Cat5</li>
        <li>Cat6</li>
    </ul>
</li>
<li>Cat7
    <ul>
        <li>Cat12</li>
    </ul>
</li>
<li>Cat8</li>
<li>Cat9
    <ul>
        <li>Cat10</li>
    </ul>
</li>
<li>Cat11</li>
</ul>

Currently, the deepest my data goes is 3 tiers, but I would like to be able to have the script do any number of tiers.

jQuery is OK for this.

Here is a class based approach. The depth is unlimited. The only requirement is that a parent must exist before a child is added.

// create class
// parent is optional Category
var Category = function (id, name, parent) {
    this.id = id;
    this.name = name;
    this.parent = null;
    this.children = [];

    if (parent) {
        parent.add(this);
    } 

};
Category.prototype.root = function() {
    if (this.parent) return this.parent.root();
    return this;
}
// find by id
Category.prototype.find = function (id) {
    if (this.id == id) return this;
    var found;

    for (var i = 0, il = this.children.length; i < il; i++) {
        if (found = this.children[i].find(id)) return found;
    }
    return null;
};

// create relationship
Category.prototype.add = function (cat) {
    cat.parent = this;
    this.children.push(cat);
};

// render list for item
Category.prototype.renderList = function ($parentElem) {
    var $nameElem = $('<li>' + this.name + '</li>').appendTo($parentElem);
    if (this.children.length) {
        this.renderChildren($('<ul />').appendTo($nameElem))
    }
}

// create child elements and add them to the parent
Category.prototype.renderChildren = function ($parentElem) {
    for (var i = 0, il = this.children.length; i < il; i++) {
        this.children[i].renderList($parentElem);
    }
}

function createCategory(id, name, parentId) {
    rootCat.find(parentId).add(new Category(id, name));
}
// add items
var rootCat = new Category(1, 'root');
createCategory(2, 'Cat1', 1);
createCategory(3, 'Cat2', 2);
createCategory(4, 'Cat3', 3);
createCategory(14, 'Cat3.5', 4);
createCategory(5, 'Cat4', 1);
createCategory(6, 'Cat5', 5);
createCategory(7, 'Cat6', 5);
createCategory(8, 'Cat7', 1);
createCategory(9, 'Cat8', 2);
createCategory(10, 'Cat9', 1);
createCategory(11, 'Cat10', 10);
createCategory(12, 'Cat11', 1);
createCategory(13, 'Cat12', 8);

// render
rootCat.renderChildren($('#cats'));

jsFiddle

Demo : http://jsfiddle.net/4JpsW/

function Category(id, categoryName, parentId){
    this.id = id;
    this.categoryName = categoryName;
    this.parentId = parentId;
}
var categories = [
new Category(1, 'Root', 0),
new Category(2, 'Cat1', 1),
new Category(3, 'Cat2', 2),
new Category(4, 'Cat3', 5),
new Category(5, 'Cat4', 1),
new Category(6, 'Cat5', 5),
new Category(7, 'Cat6', 5),
new Category(8, 'Cat7', 1),
new Category(9, 'Cat8', 2),
new Category(10, 'Cat9', 1),
new Category(11, 'Cat10', 10),
new Category(12, 'Cat11', 1),
new Category(13, 'Cat12', 8)
];

categories.sort(function(a,b){
    return a.parentId < b.parentId ? -1 : (a.parentId > b.parentId ? 1 : 0);
});
var root = document.createElement('ul'),
    currentParentId=1,
    currentParentUl=root;
for(var i=1; i < categories.length; ++i){
    if(categories[i].parentId !== currentParentId){
        currentParentId = categories[i].parentId;
        currentParentUl = document.createElement('ul');
        root.getElementsByClassName('category_'+currentParentId)[0]
            .appendChild(currentParentUl);
    }

    currentParentUl.innerHTML += '<li class="category_'+categories[i].id+'">'+categories[i].categoryName+'</li>';
}
document.body.appendChild(root);

Note 1 : Like @DanielGimenez's code, the depth is unlimited and a parent must exist before a child is added.

Note 2 : My code uses the native functions sort and getElementsByClassName to do the most expensive part (sorting and finding) to have better performance.

Note 3 : I have used classes instead of ids to avoid conflicts with other parts of your page. But if that's not a problem, using ids will increase the speed.

You can compare performance here: http://jsperf.com/creating-a-nested-list-with-an-object-array

Short and dirty jQuery. This destroys the array. Not that efficient but it's short. http://jsfiddle.net/4JpsW/

function drawList(cat) {
  r = $('<div></div>').attr('data-id', '1');
  r.append($('<ul></ul>'));
  $('body').append(r)

  while (cat.length > 0) {
    if (cat[0].parent == 0) {
      cat.splice(0, 1);
      continue;
    }

    var i = 0;
    while ($('*[data-id=' + cat[i].parent + ']').length == 0) {
      i++;
    }

    elem = $('<li></li>')
      .attr('data-id', cat[i].id)
      .text(cat[i].name);

    parent_elem = $('*[data-id=' + cat[i].parent + ']')
    if (parent_elem.find('>ul').length == 0)
      parent_elem.append($('<ul></ul>'));

    parent_elem.find('>ul').append(elem);

    cat.splice(i, 1);
  }
}

This is what i use, with it you can have multiple root parents.

JS

    function loadCatList() {
var li_str = '';

db.transaction(function (tx) {
tx.executeSql('SELECT * FROM categories c,categories_description cd WHERE c.categories_id = cd.categories_id ORDER BY categories_id', [], function (tx, results) {

var len = results.rows.length, i;
var categories=[];
for (i = 0; i < len; i++) {

var r = results.rows.item(i);

categories[r.categories_id] = r;
var catID = r.categories_id;


}

generate_tree_list=function(array, parent, level){
var has_children = false;

  for(key in array)
  {
   var value=array[key];

    if (value['parent_id'] == parent) 
    {     


      if (has_children === false)
      {

        has_children = true;  

 if(level === 0){

 li_str += '<ul>';

 }else{

 li_str += '<ul>';

 }

 level++;

 }
 li_str += '<li id="'+ value['categories_id'] +'"><a href="productsList.html?cPath='+ value['categories_id'] +'"  data-ajax="false"><h3>' + value['categories_name'] + '</h3><p>' + value['categories_image'] + '</p></a>'

 generate_tree_list(array, key, level); 

 li_str += '</li>';

 }

 }

if (has_children === true) li_str += '</ul>';

}
generate_tree_list(categories,0,0);
$('#parents').html(li_str);





});

});  

}

HTML

<div id="catItems"><script>loadCatList();</script>

 <div id="parents"></div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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