We help IT Professionals succeed at work.

Need help adding functionallity to simple Javascript expandable item tree

GrrWolfie
GrrWolfie asked
on
So I need to add the following,
1. A folder maximize and minimize link next to each folder. They'd operate by changing the "expand:" value in the array to true or false and redraw the array by calling createList(myArray). I just have no idea how to find all the parents beyond the first for this link. I'm also veeeeery new to this object passed "parenting" i'm struggling with the information available on the web on this when attempting to apply it to this code.
2. Each non-folder will have a <input type="text" value="?"> field, inside will be the full path (all parents) to that item + the item.
Example: "Ultra Purple" would show as Colors\Special\Purple\Ultra Purple
3. Instead of a list i'd like to use multiple &nbsp; tags for tabbing things over.

Many thanks, this would be a huge help i'm massively stuck.

Here's what I got so far! (credit to djon2003 for his awesome code :) )
- I get this error: "You may not start a |i] tag within a |i] tag" (replace "|" with "["), I had to change all instances of |i] to "|i]" to get it to post. To test it they'll need to be changed back. Anyone know how to get around that?

<div id="thecontainer"></div>

<script language="JavaScript">
var myArray= 
[
{name:"Colors",expand:true, children :
   [
      {name:"Blue",expand:true,children:[]},
      {name:"Yellow",expand:true,children:[]},
      {name:"Special",expand:true,children: 
         [
            {name:"Purple",expand:true,children:
               [
                  {name:"Ultra Purple",expand:true,children:[]},
               ]
            }
         ]
      }
   ]
},
{name:"Price range",expand:false,children: 
   [
      {name:"Low",expand:false,children:[]}
   ]
},
{name:"Distance",expand:false,children:[]}
];
var divContainer = document.getElementById('thecontainer');
divContainer.innerHTML = createList(myArray);
function createList(nodes, parent, level){
   if (level=="undefined"){
      level=1
   }
   var html="<ul>";
   if(parent!=undefined && parent.expand!=true){
      var html = "<ul style=\"display:none\">"
   }
   else{
      var html="<ul>"
   }
   for(var i = 0; i < nodes.length; i++) {
      //Do something  with nodes[i].name and other values
      html+="<li>";
      if (nodes[i].children.length!=0){

         //Here's where I'd add a folder expansion button, need an example! :X

      }
      html += nodes[i].name;
      if (nodes[i].children.length!=0){

         html+=createList(nodes[i].children, nodes[i], level + 1);
      }
      html+="</li>";
   }
   html+="</ul>";
   return html;
}
//myArray[Colors][]='false';createList(myArray);"
</script>

Open in new window

Comment
Watch Question

Michel PlungjanIT Expert
CERTIFIED EXPERT
Top Expert 2009

Commented:
[ i ] is italics

Just wrap in [ code ] or click Code and paste there.

I have fixed your post

Author

Commented:
* bump *

Accepting partial answers here, if I get a push in the right direction i'll go at it like a tiger! :)
CERTIFIED EXPERT
Top Expert 2015
Commented:
Here's a couple of partial answers.

1.) Assuming that you are planning on rebuilding the un-ordered lists each time a user clicks a plus or minus button, there's no point in creating sub list if you are just going to set them to display:none. Just check to see if expand=false and skip over them if it is. That simplifies things and eliminates this step:

      if(parent!=undefined && parent.expand!=true){
           var html = "<ul style=\"display:none\">"
      }

2.) Here's a sample of what you could do at this step to create a expand button:

       //Here's where i'd add a folder expansion button, need an example! :X
       html+="<input type=\"button\" value=\"+\" onclick=\"expandChild();\" />";

Author

Commented:
Good info tommyBoy, didn't even think of skipping the invisible stuff hah.

As for an expansion button, here's an example of one that'd be for the folder "Purple":
html+="<input type=\"button\" value=\"+\" onclick=\"myArray[0].children[2].children[0].expand=false; createList(myArray);\" />";

My only issue is figuring out how to dynamically generate that link :X

Or maybe i'm going about this all wrong lol.

Display before link is pressed:
+ Colors
    Blue
    Yellow
    + Special
        + Purple
            Ultra Purple
- Price range
Distance

Display after link is pressed:
+ Colors
    Blue
    Yellow
    + Special
        - Purple
- Price range
Distance

Thanks! :)
CERTIFIED EXPERT
Top Expert 2015

Commented:
This is the way I am picturing the finished tree.

Display before Purple link is pressed:
- Colors
    Blue
    Yellow
    - Special
        + Purple
+ Price range
Distance


Display after Purple link is pressed:

- Colors
    Blue
    Yellow
    - Special
        - Purple
              Ultra Purple
+ Price range
Distance

Author

Commented:
Oh yes that's correct, got a little hasty with that visual. Still pounding on the code. :P
CERTIFIED EXPERT
Top Expert 2015

Commented:
How's it going? I found some time last night to take a closer look at this. I believe I have another partial answer to offer. I have code that displays the array as depicted in the mock up in my last post. You may already be passed that point so I don't want to confuse the issue by posting it now. It was good practice, so I don't care if it's not needed. What I have not worked out yet is the click events that expand and collapse the tree nodes by changing the expand property in the array. Maybe later. Let me know where you are on this and if you need any help.

Author

Commented:
Thanks for checking in btw...

I've come a long long way and i've decided that it would be better to hide the branches than re-draw the whole thing.

At his point the branches are nested correctly in div's that belong to their parents. Their also distanced according to their level on the tree. I have some initial code for building the maximize/minimize buttons but it's in a pretty messy state so I didn't post it. I should be able to figure it out today but we'll see! :) I'll let you know if I get stuck on something.

<table><tr><td>
<div id="thecontainer"></div>
</table>

<script language="JavaScript">
var myArray= 
[
{name:"Colors",expand:true, children :
	[
		{name:"Blue",expand:true,children:[]},
		{name:"Special",expand:true,children: 
			[
				{name:"Purple",expand:true,children:
					[
						{name:"Ultra Purple",expand:true,children:[]},
					]
				}
			]
		},
		{name:"Yellow",expand:true,children:[]}
	]
},
{name:"Price range",expand:false,children: 
	[
		{name:"Low",expand:false,children:[]}
	]
},
{name:"Distance",expand:false,children:[]}
];

var lastlevel
var treeposition=new Array();
function createList(nodes, parent, level){
	if (level==undefined){level=0}
	var html=""
	for(var i=0;i<nodes.length;i++){
		html+="<img width=\""+(level*30)+"\" height=\"0\" src=\"img/all_bp.gif\">";
		if(nodes[i].children.length!=0){
			if(nodes[i].expand!=true){
				html+="<a href=\"javascript:togglebranch('')\">+</a>"+" "
				html+=nodes[i].name;
				html+="<div style=\"display:none\">";
				html+=createList(nodes[i].children,nodes[i],level+1);
				html+="</div><br>";
			}
			else{
				html+="<a href=\"javascript:togglebranch('')\">-</a>"+" "
				html+=nodes[i].name;
				html+="<div style=\"border-style:solid;border-width:1px;border-color:#aaaaaa;padding:3px\">";
				html+=createList(nodes[i].children,nodes[i],level+1);
				html+="</div>";
			}
		}
		else{ //It's a file
			html+=nodes[i].name;
			html+="<br>";
		}
	}
   return html;
}

document.getElementById("thecontainer").innerHTML=createList(myArray);

Open in new window

Author

Commented:
typo: "var lastlevel" i'm not using and won't be using.
Ok got it! Did some major clean up since I wont be doing re-drawing. Thanks tommyBoy for being there, good tips and moral support, can't put a price on that :)

<table><tr><td>
<div id="thecontainer"></div>
</table>

<script language="JavaScript">
var myArray= 
[
{name:"Colors", children :
	[
		{name:"Blue"},
		{name:"Special",children: 
			[
				{name:"Purple",children:
					[
						{name:"Ultra Purple"},
					]
				}
			]
		},
		{name:"Yellow"},
		{name:"Yellow2"}
	]
},
{name:"Price range",expand:false,children: 
	[
		{name:"Low",expand:false}
	]
},
{name:"Distance",expand:false}
];

var treepositionbynumber=new Array();
var treepositionbyname=new Array();
function createList(nodes, parent, level){
	if(level==undefined){level=0}
	var html=""
	for(var i=0;i<nodes.length;i++){
		html+="<img width=\""+(level*30)+"\" height=\"0\" src=\"img/all_bp.gif\">";
		if("children" in nodes[i]==true){ //It's a folder
			if(nodes[i].children.length!=0){
				treepositionbynumber[level]=i
				treepositionbyname[level]=nodes[i].name
				var id=""
				for(var i2=0;i2<=level;i2++){
					id+=treepositionbynumber[i2]+"-"
				}
				html+="<a href=\"javascript:togglebranch('"+id+"')\"><span id=\""+id+"link\">+</span></a>"+" "
				html+=nodes[i].name+"<br>";
				html+="<div id=\""+id+"\" style=\"display:none;border-style:solid;border-width:1px;border-color:#aaaaaa;padding:3px\">";
				html+=createList(nodes[i].children,nodes[i],level+1);
				html+="</div>";
			}
		}
		else{ //It's a file
			html+=nodes[i].name;
			html+="<br>";
		}
	}
   return html;
}

function togglebranch(id){
	var e=document.getElementById(id)
	var e2=document.getElementById(id+"link")
	if (e.style.display=="none"){
		e.style.display="block"
		e2.innerHTML="-"
	}
	else{
		e.style.display="none"
		e2.innerHTML="+"
	}
}

document.getElementById("thecontainer").innerHTML=createList(myArray);
</script>

Open in new window

Author

Commented:
I completed the code and satisfied the goals of this thread with some minor alterations in direction.
CERTIFIED EXPERT
Top Expert 2015

Commented:
That works nicely. Very efficient. More compact than what I came up with to display the tree.

I went a completely different direction. I kept the un-ordered lists and abandoned the html string. Instead, I accessed the HTML DOM directly and inserted the ULs and LIs where needed.

Thanks for the points.

Explore More ContentExplore courses, solutions, and other research materials related to this topic.