Question

...images
......|vertical
......|horizontal
...Jquery
......|UI
......|include
...quickfox

Array to process: I have folder structure Like above.And it is stored in array dirs. see below

var dirs = [ 
        "images",
        "images/vertical",
        "images/horizontal",
        "Jquery",
        "Jquery/UI",
        "Jquery/include",
        "quickfox"
        ];

Objective: How Can I make nested ul li as below.

<ul id="categorymenu">
    <li>images </li>
    <ul>
        <li>vertical</li>
        <li>horizontal</li>
    </ul>
    <li>Jquery</li>
    <ul>
        <li>UI</li>
        <li>include</li>
    </ul>
    <li>quickfox</li>
</ul>

UPDATE: XML STRUCTURE

<directory name="images">
   <file path="BBB.gif" width="500" height="282">BBB.gif</file>
   <file path="AAA.jpg" width="964" height="525">AAA.jpg</file>
   <directory name="images/vertical">
      <file path="CCC.jpg" width="964" height="525">CCC.jpg</file>
   </directory>
   <directory name="images/horizontal">
      <file path="DDD.jpg" width="964" height="525">DDD.jpg</file>      
   </directory>
</directory>

This is where I make array from xml ..

$(document).ready(function () {
    //------ READ XML -----------
    $.ajax({
        type: "GET",
        url: "___deck.xml",
        dataType: "xml",
        success: function (data) {
            my_xml = data;
            xmlDirParser(my_xml);
        }
    });
    //------ Get Files on List Change  -----------
    $("#dirlist").change(function () {
        var folder = $(this).find('option:selected').text();
        xmlFileParser(folder, my_xml);
    });
});

function xmlDirParser(my_xml) {
    $(my_xml).find('directory').each(function () {
        var dirname = $(this).attr('name');
        // $('#dirlist').append('<option value="1">'+dirname+'</option>');
        //This is where I get dirs array
    });
}
Was it helpful?

Solution 2

ORIGINAL ANSWER

If you change your data structure to multi dimensional arrays/objects can use a recursive function to loop through infinite nesting levels as follows:

var dirs = [{
    name: "images",
    subdir: [{
        name: "vertical"
    }, {
        name: "horizontal"
    }]
}, {
    name: "Jquery",
    subdir: [{
        name: "UI", subdir: [{name:'Nested 1',subdir: [{name:'Nested 2',subdir: [{name:'Nested 3'}]}]}]
    }, {
        name: "include"
    }]
}, {
    name: "quickfox"
}];

function createList( array){
    var html='<ul>';
    $.each( array, function(k, item){
        html+='<li>'+item.name;
        if( item.subdir){
            html+=createList(item.subdir);
        }
        html+='</li>';
    });
     html+='</ul>';
    return html;
}


$('body').html( createList( dirs))

DEMO: http://jsfiddle.net/AA6yb/1

REVISED ANSWER

Based on updated information that xml already has nesting, issue is how to parse xml not flat array.

Following recursively loops through all directories in xml that are children. By using find() you sinply created a flat array since find() looks for all descendents

function createList($xml) {
    var html = '';

    $xml.children('directory').each(function () {
        var $dir = $(this);
        var name= $dir.attr('name');
        html += '<li class="dir">' + parseName($dir.attr('name'));
        if ($dir.children().length) {
            html += '<ul>';
            $dir.children('file').each(function () {
                html += '<li class="file">' + $(this).attr('path') + '</li>';
            });
            /* recursively loop through children directories of this directory*/
            if( $dir.children('directory').length){
                html+=createList($dir);
            }
            html += '</ul>';
        }
    });
    html += '</li>';
    return html;
}
function parseName( name){
    if( name.indexOf('/')>-1){
        return name.split('/').pop();
    }else{
        return name;
    }
}

$('#directory_list').html(createList($(xmlData)))

HTML

<ul id="directory_list"></ul>

DEMO: http://jsfiddle.net/AA6yb/2/

OTHER TIPS

Structure of your array is not useful for creating elements, you can create an object based on the array and use this object instead.

var o = {}; 

// Creating an object based on the array elements
$.each(dirs, function (_, value) {
    if (value.indexOf('/') === -1) {
        o[value] = [];
    } else {
        var arr = value.split('/');
        o[arr[0]].push(arr[1]);
    }
});

// Creating elements

var html = '<ul id="categorymenu">';
for (key in o) {
    html += '<li>' + key + '</li>';
    if (o[key].length) {
        html += '<ul><li>' + o[key].join('</li><li>') + '</li></ul>';
    }
}
html += '</ul>';

http://jsfiddle.net/5DuDp/

Update:

I didn't notice that the intended markup is invalid, an ul element should only have li child elements, you should add the descendant ul element to the li element:

for (key in o) {
    html += '<li>' + key ;
    if (o[key].length) {
        html += '<ul><li>' + o[key].join('</li><li>') + '</li></ul>';
    }
    html += '</li>';
}
html += '</ul>';

http://jsfiddle.net/642pr/

If you must have this data structure you can use this:

$(function () {
    var dirs = [
        "images",
        "images/vertical",
        "images/horizontal",
        "Jquery",
        "Jquery/UI",
        "Jquery/include",
        "quickfox"];

    var rootList = $("<ul id='categorymenu'>").appendTo("body");
    var elements = {};

    $.each(dirs, function () {
        var parent = elements[this.substr(0, this.lastIndexOf("/"))];
        var list = parent ? parent.next("ul") : rootList;
        var textMenu= parent ? this.split("/")[1] : this;
        if (!list.length) {
            list = $("<ul>").insertAfter(parent);
        }
        var item = $("<li>").appendTo(list);
        $("<a>").text(textMenu).appendTo(item);
        elements[this] = item;
    });
});

Here is a fiddle: http://jsfiddle.net/K8SZk/3/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top