Search code examples
jqueryarraystreetreeview

How to convert array to Tree View with expand collapse functionality using jQuery?


This is quiet tricky, but some nerd can answer my query as I failed on achieving.
Let's say I have an array with below values.

globalResFiltered = [];
    globalResFiltered.push({Category:"Fruit",Subcategory:"Orange",Value:"001"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Orange",Value:"012"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Orange",Value:"006"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Mango",Value:"011"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Apple",Value:"013"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Apple",Value:"004"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Apple",Value:"010"});
    
    globalResFiltered.push({Category:"Color",Subcategory:"Silver",Value:"014"});
    globalResFiltered.push({Category:"Color",Subcategory:"Silver",Value:"015"});
    globalResFiltered.push({Category:"Color",Subcategory:"Orange",Value:"005"});
    globalResFiltered.push({Category:"Color",Subcategory:"Orange",Value:"016"});
    globalResFiltered.push({Category:"Color",Subcategory:"Red",Value:"003"});
    globalResFiltered.push({Category:"Color",Subcategory:"Red",Value:"017"});
    globalResFiltered.push({Category:"Color",Subcategory:"Red",Value:"018"});
    
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Bronze",Value:"019"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Bronze",Value:"020"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Silver",Value:"007"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Gold",Value:"002"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Gold",Value:"008"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Platinum",Value:"009"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Platinum",Value:"021"});
    
console.log(globalResFiltered);
(Please see console for array output)

Perfect, now my array has 3 main categories:
Fruit
Color
Jewellery

Each of them have Subcategories(can be repetetive):
Fruit - Orange, Mango & Apple
Color - Silver, Orange & Red
Jewellery - Bronze, Silver, Gold & Platinum
And each of those subcategories hold some values(from 001 to 021)

Now, the main part of the question. I want above array to be converted to tree view.
I already have HTML, JS & CSS code on how the output should be.
The values are hard coded.
Check below snippet

$(function () {
    $('.vertical-tree ul').hide();
    $('.vertical-tree>ul').show();
    $('.vertical-tree ul.active').show();
    $('.vertical-tree li').on('click', function (e) {
        var children = $(this).find('> ul');
        if (children.is(":visible")) children.hide('fast').removeClass('active');
        else children.show('fast').addClass('active');
        e.stopPropagation();
    });
});
/*-------------vertical-tree-view------------*/
.vertical-tree{
    padding-top: 40px;
    padding-bottom: 40px;
}
.vertical-tree ul{
    padding-left: 30px;
}
.vertical-tree li {
    margin: 0px 0;
    list-style-type: none;
    position: relative;
    padding: 20px 5px 0px 5px;
}
.vertical-tree li::before{
    content: '';
    position: absolute; 
    top: 0;
    width: 1px; 
    height: 100%;
    right: auto; 
    left: -20px;
    border-left: 2px solid #ccc;
    bottom: 50px;
}
.vertical-tree li::after{
    content: '';
    position: absolute; 
    top: 34px; 
    width: 25px; 
    height: 20px;
    right: auto; 
    left: -20px;
    border-top: 2px solid #ccc;
}
.vertical-tree li a{
    display: inline-block;
    padding: 8px 30px;
    text-decoration: none;
    background-color: #e1eafc;
    color: #000000;
    border: 1px solid #5285eb;
    font-size: 13px;
    border-radius: 4px;
}
.vertical-tree > ul > li::before, 
.vertical-tree > ul > li::after{
    border: 0;
}
.vertical-tree li:last-child::before{ 
        height: 34px;
}
.vertical-tree li a:hover, 
.vertical-tree li a:hover+ul li a {
    background-color: #5a8dee;
    color: #fff;
    border: 1px solid #5a8dee;
}
.vertical-tree li a:hover+ul li::after, 
.vertical-tree li a:hover+ul li::before, 
.vertical-tree li a:hover+ul::before, 
.vertical-tree li a:hover+ul ul::before{
    border-color:  #fbba00;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="mainDiv">
    <div class="vertical-tree" style="display:block;">
    <ul>
        <!--Fruit-->
        <li>
            <a href="javascript:void(0);">Fruit</a>
            <ul>
                <li>
                    <a href="javascript:void(0);">Orange</a>
                    <ul>
                        <li>
                            <a href="javascript:void(0);">001</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">012</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">016</a>
                        </li>
                    </ul>
                </li>
                <li>
                    <a href="javascript:void(0);">Mango</a>
                    <ul>
                        <li>
                            <a href="javascript:void(0);">011</a>
                        </li>
                    </ul>
                </li>
                <li>
                    <a href="javascript:void(0);">Apple</a>
                    <ul>
                        <li>
                            <a href="javascript:void(0);">013</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">004</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">010</a>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
        <!--Color-->
        <li>
            <a href="javascript:void(0);">Color</a>
            <ul>
                <li>
                    <a href="javascript:void(0);">Silver</a>
                    <ul>
                        <li>
                            <a href="javascript:void(0);">014</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">015</a>
                        </li>
                    </ul>
                </li>
                <li>
                    <a href="javascript:void(0);">Orange</a>
                    <ul>
                        <li>
                            <a href="javascript:void(0);">005</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">016</a>
                        </li>
                    </ul>
                </li>
                <li>
                    <a href="javascript:void(0);">Red</a>
                    <ul>
                        <li>
                            <a href="javascript:void(0);">003</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">017</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">018</a>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
        
        <!--Jewellery-->
        <li>
            <a href="javascript:void(0);">Jewellery</a>
            <ul>
                <li>
                    <a href="javascript:void(0);">Bronze</a>
                    <ul>
                        <li>
                            <a href="javascript:void(0);">019</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">020</a>
                        </li>
                    </ul>
                </li>
                <li>
                    <a href="javascript:void(0);">Silver</a>
                    <ul>
                        <li>
                            <a href="javascript:void(0);">007</a>
                        </li>
                    </ul>
                </li>
                <li>
                    <a href="javascript:void(0);">Gold</a>
                    <ul>
                        <li>
                            <a href="javascript:void(0);">002</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">008</a>
                        </li>
                    </ul>
                </li>
                <li>
                    <a href="javascript:void(0);">Platinum</a>
                    <ul>
                        <li>
                            <a href="javascript:void(0);">009</a>
                        </li>
                        <li>
                            <a href="javascript:void(0);">021</a>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
        
    </ul>
</div>


The above values are hard coded, I need something which would convert the output of array to above HTML.
This is what I have tried so far. It does not loop subcategories(Incorrect Output)
Also, not sure how to show values

var globalResFiltered = [];
    globalResFiltered.push({Category:"Fruit",Subcategory:"Orange",Value:"001"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Orange",Value:"012"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Orange",Value:"006"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Mango",Value:"011"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Apple",Value:"013"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Apple",Value:"004"});
    globalResFiltered.push({Category:"Fruit",Subcategory:"Apple",Value:"010"});
    
    globalResFiltered.push({Category:"Color",Subcategory:"Silver",Value:"014"});
    globalResFiltered.push({Category:"Color",Subcategory:"Silver",Value:"015"});
    globalResFiltered.push({Category:"Color",Subcategory:"Orange",Value:"005"});
    globalResFiltered.push({Category:"Color",Subcategory:"Orange",Value:"016"});
    globalResFiltered.push({Category:"Color",Subcategory:"Red",Value:"003"});
    globalResFiltered.push({Category:"Color",Subcategory:"Red",Value:"017"});
    globalResFiltered.push({Category:"Color",Subcategory:"Red",Value:"018"});
    
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Bronze",Value:"019"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Bronze",Value:"020"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Silver",Value:"007"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Gold",Value:"002"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Gold",Value:"008"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Platinum",Value:"009"});
    globalResFiltered.push({Category:"Jewellery",Subcategory:"Platinum",Value:"021"});
    
    var tempCat = "";
    var tempSubCat = "";
    
    $(globalResFiltered).each(function(i,v){
                    
        if(i==0 || i==1 || i==2){debugger;}
        if(tempCat != v.Category)
        {
            $("#myTree").append("<ul><li><a class='c_"+v.Category+"'>"+v.Category+"</a></li></ul>");
            $(".c_"+v.Category).after("<ul><li id='x_"+i+"_"+v.Category+"_"+v.Subcategory+"'><a>"+v.Subcategory+"</a></li></ul>");
        }
        else
        {
            $("#x_0"+v.Category+"_"+v.Subcategory).after("<li id='x_"+i+"_"+v.Category+"_"+v.Subcategory+"'><a>"+v.Subcategory+"</a></li>");
        }
        tempCat = v.Category;
                    
                    
                    
    });






$(function () {
    $('.vertical-tree ul').hide();
    $('.vertical-tree>ul').show();
    $('.vertical-tree ul.active').show();
    $('.vertical-tree li').on('click', function (e) {
        var children = $(this).find('> ul');
        if (children.is(":visible")) children.hide('fast').removeClass('active');
        else children.show('fast').addClass('active');
        e.stopPropagation();
    });
});
/*-------------vertical-tree-view------------*/
.vertical-tree{
    padding-top: 40px;
    padding-bottom: 40px;
}
.vertical-tree ul{
    padding-left: 30px;
}
.vertical-tree li {
    margin: 0px 0;
    list-style-type: none;
    position: relative;
    padding: 20px 5px 0px 5px;
}
.vertical-tree li::before{
    content: '';
    position: absolute; 
    top: 0;
    width: 1px; 
    height: 100%;
    right: auto; 
    left: -20px;
    border-left: 2px solid #ccc;
    bottom: 50px;
}
.vertical-tree li::after{
    content: '';
    position: absolute; 
    top: 34px; 
    width: 25px; 
    height: 20px;
    right: auto; 
    left: -20px;
    border-top: 2px solid #ccc;
}
.vertical-tree li a{
    display: inline-block;
    padding: 8px 30px;
    text-decoration: none;
    background-color: #e1eafc;
    color: #5a8dee;
    border: 1px solid #5285eb;
    font-size: 13px;
    border-radius: 4px;
}
.vertical-tree > ul > li::before, 
.vertical-tree > ul > li::after{
    border: 0;
}
.vertical-tree li:last-child::before{ 
        height: 34px;
}
.vertical-tree li a:hover, 
.vertical-tree li a:hover+ul li a {
    background-color: #5a8dee;
    color: #fff;
    border: 1px solid #5a8dee;
}
.vertical-tree li a:hover+ul li::after, 
.vertical-tree li a:hover+ul li::before, 
.vertical-tree li a:hover+ul::before, 
.vertical-tree li a:hover+ul ul::before{
    border-color:  #fbba00;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Incorrect, it only shows one subcategory
<div class="vertical-tree" id="myTree">
</div>

I have tried checking below reference, but in vain.
https://www.jqueryscript.net/table/jQuery-Plugin-For-Displaying-A-Tree-Of-Data-In-A-Table-treetable.html https://www.jqueryscript.net/demo/simple-tree-table/


Thanks in advance for your help 😁'

Solution

  • I would do this in two steps:

    • Convert the data structure to a hierarchical one: toHierarchy
    • Turn that hierarchy to a hierarchy of DOM elements: createList

    Here is the code:

    function toHierarchy(data, ...fields) {
        let map = {};
        for (let item of data) {
            let node = map;
            for (let field of fields) {
                let value = item[field];
                node = (node[value] ??= {});
            }
        }
        return map;
    }
    
    function createList(hierarchy) {
        return $("<ul>").css({display: "none"}).append(
            Object.entries(hierarchy).flatMap(([value, children]) => $("<li>").append(
                 $("<a>", { href: "javascript:void(0);" }).text(value),
                 createList(children)
            ))
        );
    }
    
    // Your sample data:
    const globalResFiltered = [{Category:"Fruit",Subcategory:"Orange",Value:"001"},{Category:"Fruit",Subcategory:"Orange",Value:"012"},{Category:"Fruit",Subcategory:"Orange",Value:"006"},{Category:"Fruit",Subcategory:"Mango",Value:"011"},{Category:"Fruit",Subcategory:"Apple",Value:"013"},{Category:"Fruit",Subcategory:"Apple",Value:"004"},{Category:"Fruit",Subcategory:"Apple",Value:"010"},{Category:"Color",Subcategory:"Silver",Value:"014"},{Category:"Color",Subcategory:"Silver",Value:"015"},{Category:"Color",Subcategory:"Orange",Value:"005"},{Category:"Color",Subcategory:"Orange",Value:"016"},{Category:"Color",Subcategory:"Red",Value:"003"},{Category:"Color",Subcategory:"Red",Value:"017"},{Category:"Color",Subcategory:"Red",Value:"018"},{Category:"Jewellery",Subcategory:"Bronze",Value:"019"},{Category:"Jewellery",Subcategory:"Bronze",Value:"020"},{Category:"Jewellery",Subcategory:"Silver",Value:"007"},{Category:"Jewellery",Subcategory:"Gold",Value:"002"},{Category:"Jewellery",Subcategory:"Gold",Value:"008"},{Category:"Jewellery",Subcategory:"Platinum",Value:"009"},{Category:"Jewellery",Subcategory:"Platinum",Value:"021"}];   
    
    // Convert data structure to a hierarchical one:
    const hierarchy = toHierarchy(globalResFiltered, "Category", "Subcategory", "Value");
    
    $(function () {
        // Added this line:
        $(".vertical-tree").empty().append(createList(hierarchy));
        $('.vertical-tree ul').hide();
        $('.vertical-tree>ul').show();
        $('.vertical-tree ul.active').show();
        $('.vertical-tree li').on('click', function (e) {
            var children = $(this).find('> ul');
            if (children.is(":visible")) children.hide('fast').removeClass('active');
            else children.show('fast').addClass('active');
            e.stopPropagation();
        });
    });
    /*-------------vertical-tree-view------------*/
    .vertical-tree{
        padding-top: 40px;
        padding-bottom: 40px;
    }
    .vertical-tree ul{
        padding-left: 30px;
    }
    .vertical-tree li {
        margin: 0px 0;
        list-style-type: none;
        position: relative;
        padding: 20px 5px 0px 5px;
    }
    .vertical-tree li::before{
        content: '';
        position: absolute; 
        top: 0;
        width: 1px; 
        height: 100%;
        right: auto; 
        left: -20px;
        border-left: 2px solid #ccc;
        bottom: 50px;
    }
    .vertical-tree li::after{
        content: '';
        position: absolute; 
        top: 34px; 
        width: 25px; 
        height: 20px;
        right: auto; 
        left: -20px;
        border-top: 2px solid #ccc;
    }
    .vertical-tree li a{
        display: inline-block;
        padding: 8px 30px;
        text-decoration: none;
        background-color: #e1eafc;
        color: #000000;
        border: 1px solid #5285eb;
        font-size: 13px;
        border-radius: 4px;
    }
    .vertical-tree > ul > li::before, 
    .vertical-tree > ul > li::after{
        border: 0;
    }
    .vertical-tree li:last-child::before{ 
            height: 34px;
    }
    .vertical-tree li a:hover, 
    .vertical-tree li a:hover+ul li a {
        background-color: #5a8dee;
        color: #fff;
        border: 1px solid #5a8dee;
    }
    .vertical-tree li a:hover+ul li::after, 
    .vertical-tree li a:hover+ul li::before, 
    .vertical-tree li a:hover+ul::before, 
    .vertical-tree li a:hover+ul ul::before{
        border-color:  #fbba00;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="mainDiv">
        <div class="vertical-tree"></div>
    </div>