PDA

View Full Version : The ideal tree-style menu



fambi
01-03-2007, 08:10 AM
I am looking for a tree-style menu which accomplsihes the following:

1. Uses normal ol, ul, li for markup/
2. Opening one member closes down its siblings.
3. Has some way to remember the state or use a particular tag to open up the tree on to a certain location.
4. Does not require the use of images.
5. And, of course, is as cross-browser as possible.

Am i living in a dream or does something like this exist?

fambi
01-03-2007, 08:12 AM
http://www.dynamicdrive.com/dynamicindex1/navigate1.htm does an excellent job at mearing all the requirements.

But how can it be changes so that opening a folder causes sibling folders to be closed?

ddadmin
01-03-2007, 08:37 AM
Well, there is no built in way to collapse the previous sibling when another one is expanded. However, a little hacking should do the trick. Inside the .js file, find the function:


ddtreemenu.expandSubTree=function(treeid, ulelement){
ddtreemenu.flatten(treeid, "contract")
"
"
}

The line in red is new. This should work, but it's untested. :)

fambi
01-03-2007, 09:50 AM
Thanks for your help, but that caused an error and the entire menu to be opened up.

Your further help would really be appreciated.

ddadmin
01-03-2007, 10:20 AM
Ok, there was a typo in the code I posted above (which has been corrected), though it doesn't matter- it didn't work. lol Looks like such a feature (collapse previous sibling) will take a little more than just a few lines of hacking to achieve. This means I'll have to update this script officially, which by my current schedule is probably a couple of weeks away.

fambi
01-03-2007, 01:30 PM
Thanks.

On a seperate note... i've been looking into your script more carefully and wandered...

Wouldn't it make more sense for the parents to be given a className of open or closed (as opposed to rel="xxx") and then the individual sections could be styled according to class names in the style sheet.

I am no JS-guy, so please correct me if i am wrong, but wouldn't this significantly reduce the size of the script and also give more design control through the stylesheet?

mike_p
01-03-2007, 01:37 PM
Looks like such a feature (collapse previous sibling) will take a little more than just a few lines of hacking to achieve.

I haven't looked at the script itself so this is very speculative, but can you set all siblings to closed before opening the one that should be open?

fambi
01-03-2007, 05:28 PM
I haven't looked at the script itself so this is very speculative...
Lol!!!

fambi
01-03-2007, 05:47 PM
Ok... I went ahead and modified the script significantly so that all styling is in the stylesheet and that a section of the tree is recognised as being open or closed through the class name (as opposed to the rel="" tag).

It still works the same as before, except that a lot of repetitive code has been removed. However, as i am not very well versed in JS, it would be nice if the author could have a look at the new script. If it has mistakes, then please let me know and, if not, then consider modifying the original script to reflect them.

The new script:



/***********************************************
* Simple Tree Menu- © Dynamic Drive DHTML code library (www.dynamicdrive.com)
* This notice MUST stay intact for legal use
* Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
***********************************************/

var persisteduls=new Object()
var ddtreemenu=new Object()

//////////No need to edit beyond here///////////////////////////

ddtreemenu.createTree=function(treeid, enablepersist, persistdays)
{
var ultags=document.getElementById(treeid).getElementsByTagName("ul")
if(typeof persisteduls[treeid]=="undefined")
{
persisteduls[treeid]=(enablepersist==true && ddtreemenu.getCookie(treeid)!="")? ddtreemenu.getCookie(treeid).split(",") : ""
}
for (var i=0; i<ultags.length; i++)
{
ddtreemenu.buildSubTree(treeid, ultags[i], i)
}
if(enablepersist==true) //if enable persist feature
{
var durationdays=(typeof persistdays=="undefined")? 1 : parseInt(persistdays)
ddtreemenu.dotask(window, function(){ddtreemenu.rememberstate(treeid, durationdays)}, "unload") //save opened UL indexes on body unload
}
}

ddtreemenu.buildSubTree=function(treeid, ulelement, index)
{
if(typeof persisteduls[treeid]=="object")//if cookie exists (persisteduls[treeid] is an array versus "" string)
{
if(ddtreemenu.searcharray(persisteduls[treeid], index))
{
ulelement.parentNode.className="open";
}
else
{
ulelement.parentNode.className="closed";
}
}
else if(ulelement.parentNode.className==null || ulelement.parentNode.className==false) //if no cookie and UL has NO rel attribute explicted added by user
{
ulelement.parentNode.className="closed";
}
else if(ulelement.parentNode.className=="open") //else if no cookie and this UL has an explicit rel value of "open"
{
ddtreemenu.expandSubTree(treeid, ulelement) //expand this UL plus all parent ULs (so the most inner UL is revealed!)
}

ulelement.parentNode.onclick=function(e)
{
var submenu=this.getElementsByTagName("ul")[0];
if(submenu.parentNode.className=="closed")
{
ulelement.parentNode.className="open";
}
else if(submenu.parentNode.className=="open")
{
ulelement.parentNode.className="closed";
}
ddtreemenu.preventpropagate(e)
}

ulelement.onclick=function(e)
{
ddtreemenu.preventpropagate(e);
}
}

ddtreemenu.expandSubTree=function(treeid, ulelement) //expand a UL element and any of its parent ULs
{
var rootnode=document.getElementById(treeid);
var currentnode=ulelement;
currentnode.style.display="block";
while (currentnode!=rootnode)
{
if(currentnode.tagName=="ul"); //if parent node is a UL, expand it too
{
currentnode.parentNode.className="open";
}
currentnode=currentnode.parentNode;
}
}

ddtreemenu.flatten=function(treeid, action)//expand or contract all UL elements
{
var ultags=document.getElementById(treeid).getElementsByTagName("ul")
for (var i=0; i<ultags.length; i++)
{
ultags[i].parentNode.className=(action=="expand")? "open" : "closed";
}
}

ddtreemenu.rememberstate=function(treeid, durationdays)//store index of opened ULs relative to other ULs in Tree into cookie
{
var ultags=document.getElementById(treeid).getElementsByTagName("ul")
var openuls=new Array()
for (var i=0; i<ultags.length; i++)
{
if(ultags[i].parentNode.className=="open") openuls[openuls.length]=i //save the index of the opened UL (relative to the entire list of ULs) as an array element
}
if(openuls.length==0) //if there are no opened ULs to save/persist
{
openuls[0]="none open" //set array value to string to simply indicate all ULs should persist with state being closed
}
ddtreemenu.setCookie(treeid, openuls.join(","), durationdays) //populate cookie with value treeid=1,2,3 etc (where 1,2... are the indexes of the opened ULs)
}

////A few utility functions below//////////////////////

ddtreemenu.getCookie=function(Name) //get cookie value
{
var re=new RegExp(Name+"=[^;]+", "i"); //construct RE to search for target name/value pair
if(document.cookie.match(re))
{//if cookie found
return document.cookie.match(re)[0].split("=")[1] //return its value
}
return ""
}

ddtreemenu.setCookie=function(name, value, days) //set cookei value
{
var expireDate = new Date()
//set "expstring" to either future or past date, to set or delete cookie, respectively
var expstring=expireDate.setDate(expireDate.getDate()+parseInt(days))
document.cookie = name+"="+value+"; expires="+expireDate.toGMTString()+"; path=/";
}

ddtreemenu.searcharray=function(thearray, value) //searches an array for the entered value. If found, delete value from array
{
var isfound=false
for (var i=0; i<thearray.length; i++)
{
if(thearray[i]==value)
{
isfound=true
thearray.shift() //delete this element from array for efficiency sake
break
}
}
return isfound
}

ddtreemenu.preventpropagate=function(e) //prevent action from bubbling upwards
{
if(typeof e!="undefined")
{
e.stopPropagation()
}
else
{
event.cancelBubble=true
}
}

ddtreemenu.dotask=function(target, functionref, tasktype) //assign a function to execute to an event handler (ie: onunload)
{
var tasktype=(window.addEventListener)? tasktype : "on"+tasktype
if(target.addEventListener)
{
target.addEventListener(tasktype, functionref, false)
}
else if(target.attachEvent)
{
target.attachEvent(tasktype, functionref)
}
}


The CSS



.treeview
{
width:150px;
}

/*CSS for Simple Tree Menu*/
.treeview ul
{
margin: 0;
padding: 0;
}

/*Style for LI elements in general (excludes an LI that contains sub lists)*/
.treeview li
{
list-style-type: none;
padding-left: 12px;
margin-bottom: 3px;
}

.treeview li a
{
background: white url(arrow.gif) no-repeat left center;
padding-left: 8px;
}

/* Style for LI that contains sub lists (other ULs). */
.treeview li.open, .treeview li.closed
{
cursor: hand !important;
cursor: pointer !important;
}

/*Style for ULs that are children of LIs (submenu) */
.treeview li.closed ul
{
display: none; /*Hide them by default. Don't delete. */
}

/*Style for LIs of ULs that are children of LIs (submenu) */
.treeview .open ul li, .treeview .closed ul li
{
cursor: default;
}

li.closed
{
background: white url(plus_sign.gif) no-repeat left 0.4em;
}

li.closed ul
{
display: none;
}

li.open
{
background: white url(minus_sign.gif) no-repeat left 0.4em;
}

li.open ul
{
display: block;
}


Making siblings close upon the opening of an item is still a feature request though!!!

mike_p
01-05-2007, 08:16 AM
Lol!!!

May be you don't have much experience in coding these things. There is the simple concept of first iterating through all the siblings setting them to closed. This way you don't need to know which one to close. Then you open the one you want open.

DDAdmin's suggestion attempted to first open the new tree then closed the specified old tree.

The concept is sound even without me having to trawl through the particular script myself.

fambi
01-05-2007, 10:00 AM
Sorry if my message came across wrong.

The concept is sound, but the application of the concept is what is needed and that would need the script to be looked at.

spacefish
05-29-2007, 09:25 AM
Judging by a search I've done to find an answer to this question, there seems to be a number of other people who would also like to have a previously open folder collapse when opening a new one.

I've spent the last 4 - 5 days searching for a tree menu that does all of what Simple Tree does and there is nothing (that I've found) that works nearly as well - so to have it also be able to do this new trick would be awesome! :)

abbe
08-19-2008, 07:48 AM
So, has this been solved?

I need this reallybaaaaaaaad...

abbe
10-15-2008, 12:25 PM
Ok, there was a typo in the code I posted above (which has been corrected), though it doesn't matter- it didn't work. lol Looks like such a feature (collapse previous sibling) will take a little more than just a few lines of hacking to achieve. This means I'll have to update this script officially, which by my current schedule is probably a couple of weeks away.

Any progress with this? :rolleyes: