Overview
One of the frequent problems I have to solve is displaying a nested tree with collapseable branches. Solutions do exists including one called dtree but that ran into problems with Firefox when the tree was large (an alert asking user if they want to continue the script). Also it didn't allow me to insert additional nodes etc using Ajax whatever. This is my solution, a combination of HTML Lists/Javascript and CSS. PHP can create the lists from whatever source (arrays, database trees whatever).
Setup
For the purposes of this explanation I will create the HTML using the $_SESSION variable.
The directory/file structure is
Base Dir
..index.php
..css
....tree.css
..img
....<images here>
..javascript
....tree.js
Obviously if you change the paths you will have to change the CSS and php files if you wish to try this (path names at present are relative).
We need some images which unfortunately I cannot post here. I use 16x16 transparent gifs. The images in question are...
base.gif (a root image),
line.gif (a vertical line),
nolines_plus.gif (a square box with + inside),
nolines_minus.gif (a square box with - inside),
join.gif (a horizontal line),
folder.gif (a standard folder image),
folder_open.gif (a standard open folder image),
gfolder.gif (a grey copy of the folder image),
gfolder_open.gif (a grey copy of the open folder image),
page.gif (a 'page' icon)
The Code
base_path/index.php
Code: Select all
<?php
function cssTreeList($data,$first=0)
{
static $level=0;
static $node_no=1;
$node_no++;
$out='';
if (!$first) $out.='<ul style="display:none;" id="subnode'.($node_no-1).'">';
ksort($data);
foreach ($data as $key=>$value) {
if (is_array($value)) {
if (count($value)) {
$out.='<li class="closed" id="node'.$node_no.'"><a href="javascript:treeToggle('.$node_no.',\'node\');" class="tclosed" id="nodelink'.($node_no).'">'.$key.'</a>';
} else {
$out.='<li class="closed" id="node'.$node_no.'"><a href="javascript:treeToggle('.$node_no.',\'node\');" class="tgclosed" id="nodelink'.($node_no).'">'.$key.'</a>';
}
$out.=cssTreeList($value);
$out.='</li>';
} else {
$out.='<li class="opened"><span class="tpage">'.$key.' = '.$value.'</span></li>';
}
}
if (!$first) $out.='</ul>';
return $out;
}
$_SESSION['lvl1']=array();
$_SESSION['lvl2']=array();
$_SESSION['lvl3']=array();
$_SESSION['lvl4']=array();
$_SESSION['lvl5']='Page5';
$_SESSION['lvl6']='Page6';
$_SESSION['lvl1']['lvl1_1']=array();
$_SESSION['lvl1']['lvl1_2']=array();
$_SESSION['lvl1']['lvl1_3']=array();
$_SESSION['lvl1']['lvl1_4']=array();
$_SESSION['lvl1']['lvl1_5']='Page 1.5';
$_SESSION['lvl1']['lvl1_6']='Page 1.6';
$_SESSION['lvl2']['lvl2_1']=array();
$_SESSION['lvl2']['lvl2_2']=array();
$_SESSION['lvl2']['lvl2_3']=array();
$_SESSION['lvl2']['lvl2_4']=array();
$_SESSION['lvl2']['lvl2_5']='Page 2.5';
$_SESSION['lvl2']['lvl2_6']='Page 2.6';
$_SESSION['lvl2']['lvl2_3']['lvl2_3_1']='Page 2_3_1';
$_SESSION['lvl2']['lvl2_4']['lvl2_3_2']='Page 2_3_2';
$_SESSION['lvl3']['lvl3_1']=array();
$_SESSION['lvl3']['lvl3_2']=array();
$_SESSION['lvl3']['lvl3_3']=array();
$_SESSION['lvl3']['lvl3_4']=array();
$_SESSION['lvl3']['lvl3_5']='Page 3.5';
$_SESSION['lvl3']['lvl3_6']='Page 3.6';
$_SESSION['lvl4']['lvl4_1']=array();
$_SESSION['lvl4']['lvl4_2']=array();
$_SESSION['lvl4']['lvl4_3']=array();
$_SESSION['lvl4']['lvl4_4']=array();
$_SESSION['lvl4']['lvl4_5']='Page 4.5';
$_SESSION['lvl4']['lvl4_6']='Page 4.6';
if (!empty($_SESSION)) {
$out.='<ul class="treelist"><li class="root">_SESSION<ul class="treenodes">';
$out.=cssTreeList($_SESSION,1);
$out.='</ul></li></ul>';
}
?>
<html>
<head>
<title>Css Tree List</title>
<script src="./javascript/tree.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="./css/tree.css" />
</head>
<body>
<?php echo($out); ?>
</body>
</html>Code: Select all
function treeToggle(node,identifier)
{
var cname='';
var element=document.getElementById(identifier+node);
if (element) {
switch (element.className) {
case 'opened':
element.className='closed';
break;
case 'closed':
element.className='opened';
break;
}
cname=element.className;
}
element=document.getElementById(identifier+'link'+node);
if (element) {
switch (element.className) {
case 'topened':
element.className='tclosed';
break;
case 'tclosed':
element.className='topened';
break;
case 'tgopened':
element.className='tgclosed';
break;
case 'tgclosed':
element.className='tgopened';
break;
}
}
element=document.getElementById('sub'+identifier+node);
if (element) {
switch (cname) {
case 'opened':
element.style.display='block';
break;
case 'closed':
element.style.display='none';
break;
}
}
}Code: Select all
ul.treelist
{
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
color: #000;
white-space: nowrap;
list-style-type:none;
padding:0;
}
ul.treelist li.root:before
{
content:url(../img/base.gif);
}
ul.treenodes
{
list-style-type: none;
padding: 0;
}
ul.treenodes ul {
list-style-type:none;
padding:0;
}
ul.treenodes li
{
padding:0 0 0 15px;
background: url(../img/line.gif) repeat-y 0 0;
}
ul.treenodes li:last-child {
background-repeat: no-repeat;
}
ul.treenodes li.closed a.tclosed
{
margin:-15px;
padding: 3px 0 0 0px;
background:url(../img/nolines_plus.gif) no-repeat 0 -0.15em;
text-decoration:none;
color:#00F;
}
ul.treenodes li.opened span.tpage
{
margin:-15px;
padding: 3px 0 0 0px;
text-decoration:none;
color:#00F;
}
ul.treenodes li.closed a.tgclosed
{
margin:-15px;
padding: 3px 0 0 0;
background:url(../img/nolines_plus.gif) no-repeat 0 -0.15em;
text-decoration:none;
color:#00F;
}
ul.treenodes li.opened a.topened
{
margin:-15px;
padding: 3px 0 0 0px;
background:url(../img/nolines_minus.gif) no-repeat 0 -0.15em;
text-decoration:none;
color:#00F;
}
ul.treenodes li.opened a.tgopened
{
margin:-15px;
padding: 3px 0 0 0;
background:url(../img/nolines_minus.gif) no-repeat 0 -0.15em;
text-decoration:none;
color:#00F;
width:100%;
}
li a.tclosed:before {
content: url(../img/join.gif) url(../img/folder.gif);
}
li a.topened:before {
vertical-align:middle;
content: url(../img/join.gif) url(../img/folder_open.gif);
}
li a.tgopened:before {
vertical-align:middle;
content: url(../img/join.gif) url(../img/gfolder_open.gif);
}
li a.tgclosed:before {
vertical-align:middle;
content: url(../img/join.gif) url(../img/gfolder.gif);
}
li span.tclosed:before {
content: url(../img/join.gif) url(../img/gfolder_open.gif);
}
li span.tpage:before {
content: url(../img/join.gif) url(../img/page.gif);
}
ul.treenodes li.closed span.tclosed
{
margin:-15px;
padding: 3px 0 0 0;
text-decoration:none;
color:#000;
}