ScuffedCoder
ScuffedCoder

Reputation: 408

Treeview only allow one parent node to be fully expanded

I am using SmartAdmin to create a simple treeview. The issue at hand is when I expand one parent node, I want to minimize any other ones so that at max only one node can fully expand to prevent width issues.

I have started by providing an attribute to the li called "expanded = false" and on expanding it get set to true.

This code below is what handles the hiding and showing of elements

$('.tree > ul').attr('role', 'tree').find('ul').attr('role', 'group');
    $('.tree').find('li:has(ul)').addClass('parent_li').attr('role', 'treeitem').find(' > span').attr('title', 'Collapse this branch').on('click', function (e) {
        var children = $(this).parent('li.parent_li').find(' > ul > li');
        var test = $(this).parent('li.parent_li').find(' > ul');
        var parent = $(this).parent('li.parent_li');
        if (children.is(':visible')) {
            children.hide('fast');
            parent.attr("expanded", 'false')
            $(this).attr('title', 'Expand this branch').find(' > i').removeClass().addClass('fa fa-lg fa-plus-circle');
        } else {
            children.show('fast');
            test.attr("style", 'display: grid')
            parent.attr("expanded", 'true')
           
            $(this).attr('title', 'Collapse this branch').find(' > i').removeClass().addClass('fa fa-lg fa-minus-circle');
            
        }
        
        e.stopPropagation();
    });

I thought maybe I could do something like looping through each LI element in the UL and hide based on that but the approach isn't quite there because how would we ignore the newly opened node.

$("#ULLevel0node0").find("li").each(function () {
                if ($(this).attr("expanded") == "true") {
                    children.hide('fast');
                }
            });

You can see that my main node is called ULLevel0Node0 which contains all the nodes. And then 4 LI elements within this.

I've also tried the following:

$('.tree').find(' > ul > li > ul').each(function () {
            if (($(this).attr("style") == "display: grid")) {
                $(this).attr("style", "display:none")
                console.log("it shouldve changed display")
            }
        });

Now this does kind of work but it closes everything even if you click on a sub node.

Upvotes: 0

Views: 607

Answers (1)

Leo
Leo

Reputation: 1027

I think what's easiest here is to hide all the UL groups belonging to each parent on the "on" click function, so they go to their beginning state with a "plus" sign and then the click just opens the parent accordingly, something like this:

//Sets tree's to collapsed on init
$('.tree:first-child > ul > li > ul').each(function(event, element){
    $(element).prev().attr('title', 'Expand this branch').find(' > i').removeClass().addClass('fa fa-lg fa-plus-circle');
    $(element).find('> li').hide();
});

$('.tree > ul').attr('role', 'tree').find('ul').attr('role', 'group');

//Tree event on click of span
$('.tree').find('li:has(ul)').addClass('parent_li').attr('role', 'treeitem').find(' > span').attr('title', 'Collapse this branch').on('click', function (e) {

    if($(this).closest('ul').parent().hasClass('tree')){
        //Sets tree parent children to hide and span's to initial state
        $(this).closest('.tree').find(' > ul > li > ul').each(function(event, element){
            $(element).prev().attr('title', 'Expand this branch').find(' > i').removeClass().addClass('fa fa-lg fa-plus-circle');
            $(element).find('> li').hide();
        });
    }

    var children = $(this).parent('li.parent_li').find(' > ul > li');
    var test = $(this).parent('li.parent_li').find(' > ul');
    var parent = $(this).parent('li.parent_li');
    if (children.is(':visible')) {
        children.hide('fast');
        parent.attr('expanded', 'false')
        $(this);
    } else {
        children.show('fast');
        test.attr('style', 'display: grid')
        parent.attr('expanded', 'true')
       
        $(this).attr('title', 'Collapse this branch').find(' > i').removeClass().addClass('fa fa-lg fa-minus-circle');
        
    }
    
    e.stopPropagation();
});

Also remember you can add classes to your elements, with which you can handle and manage better your elements with jQuery selectors, I wouldn't recommend either having one single function to control all of your trees in the page, as it can get really messy quickily, also you can try to create functions outside of this whole "on" click event, for example having:

$('.tree.simple-view').on('click', function(){

});

$('.tree.organizing-view').on('click', function(){

});

That's why on the code I used the $(this).closest('.tree'), to only affect the tree you are clicking on, otherwise it will have put the functionality on both trees (left and right).

I would also recommend to use only 'single quotes', or only "double quotes", and don't be afraid to put comments in your code: it really helps people (and also you) understand better what's happening.

Upvotes: 1

Related Questions