Build a List, Get a Tree
Custom Tags Aninhadas

Build a List, Get a Tree

How to create an outlined tree from <ul> and <li> tags, CSS and JavaScript

 

Cláudio Alexandre da Costa Dias«

Introduction

Hierarchical data can be fairly represented using <ul> and <li> HTML tags. For instance, in order to represent a company?s organization chart, we could write:

 

Text Box: 	<ul>
		<li><span>Division</span>
			<ul>
				<li><span>Department</span>
					<ul>
						<li><span>Employee 1</span></li>
						<li><span>Employee 2</span></li>
						<li><span>Employee 3</span></li>
						...
					</ul>
				</li>
				...
			</ul>		
		</li>
		...
	</ul>

Viewing it in a browser:

 


 

With some CSS statements, we can give this list a much better aspect.

In this tutorial, we?ll learn how to convert this list into an outlined tree control, using an elaborated CSS and some JavaScript code.

The tree control, or tree, shows you the relationships among nodes and provides items expand/collapse control.

 


Implementation

Let?s build our tree from Megaputz company organization chart, displayed below:

 

Megaputz Inc.

Division

Department

Employee

CEO

 

 

Industry

Engineering

Axel Lissmann

Claudia Kuhlmann

Ronald Lorenz

Thorsten Goeckeler

Manufacturing

Hardy Mums

Mike O'Phone

Peter Piper

Administrative

Finance

Donna Hood

Fanny Farmer

Rob Banks

Human Resources

Carrie Mehome

Ella Mentary

Jerry Atrix

Support

Customer Service

Mahatma Coate

Moe Bilhome

 

Main HTML

In order to build tree.htm page, let?s use the following HTML code:

Text Box: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>
<head>
	<title>Megaputz Inc.</title>
	<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
</head>

<body>

</body>
</html>


Building HTML List

To build the list, we will use <div>, <h3>, <ul>, <li> and <span> tags. Thus, from organization chart, we get:

 

Text Box: <div class="tree">
	<h3><span>Megaputz Inc.</span></h3>
	<ul>
		<li><span>CEO</span></li>
		<li><span>Industry</span>
			<ul>
				<li><span>Engineering</span>
					<ul>
						<li><span>Axel Lissmann</span></li>
						<li><span>Claudia Kuhlmann</span></li>
						<li><span>Ronald Lorenz</span></li>
						<li><span>Thorsten Goeckeler</span></li>
					</ul>
				</li>
				<li><span>Manufacturing</span>
					<ul>
						<li><span>Hardy Mums</span></li>
						<li><span>Mike O'Phone</span></li>
					</ul>
				</li>
			</ul>		
		</li>
		<li class="collapsed"><span>Administrative</span>
			<ul>
				<li><span>Finance</span>
					<ul>
						<li><span>Donna Hood</span></li>
						<li><span>Fanny Farmer</span></li>
					</ul>
				</li>
				<li class="collapsed"><span>Human Resources</span>
					<ul>
						<li><span>Carrie Mehome</span></li>
						<li><span>Ella Mentary</span></li>
						<li><span>Jerry Atrix</span></li>
					</ul>
				</li>
			</ul>
		</li>
		<li><span>Support</span>
			<ul>
				<li><span>Customer Service</span>
					<ul>
						<li><span>Mahatma Coate</span></li>
						<li><span>Moe Bilhome</span></li>
					</ul>
				</li>
			</ul>
		</li>
	</ul>
</div>

We shall place this code on page tree.htm inside <body> tag.

 


Some explanations:

  1. The tree?s root is identified by <h3>...</h3> tag

2.      The <div class="tree">...</div> tag shall exist in order to group the tree and it?s root in the same block

3.      The <span>...</span> tag was used inside <li>...</li> tags. It shall group all item content (images, links, texts)

4.      If we want to define a collapsed item, we shall define the <li> class as collapsed

 

Our HTML page is done! Viewing it in a browser:

 


JavaScript functions

To keep our source code simple, and as long as we cannot use CSS 2 to render our tree ? remember that IE does not correctly implement this CSS version ? we shall use some JavaScript code.

In addition, we must code sub-trees expand/collapse functionality.

First, we create tree.js file, add to our page a calling to this file and also call getTrees() function:

 

Text Box: <html>
...
<head>
	<title>Untitled</title>
	<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
	<script language="JavaScript" type="text/javascript" src="tree.js"></script>
</head>
...
<script language="JavaScript" type="text/javascript">
getTrees();
</script>

</body>
</html>

 


Identifying lists to be converted into trees ? getTrees()

Inside tree.js file, we write getTrees() function to find all <div> tags having class tree.

For each <div> found, this function will point onclick event to treeAction() function and will retrieve all <ul> tags. Also, it will call another function ? ul2tree() ? that converts each <ul> found to a tree.

Text Box: function getTrees() {
	// retrieve all <div>
	var treeObjs = document.getElementsByTagName('DIV');
	for (var i=0;i<treeObjs.length;i++) 
		// class = 'tree' ?
		if (treeObjs[i].className == 'tree') {
			// point onclick event to treeAction() function
			treeObjs[i].onclick = treeAction;
			// loop over all <div> children using childNodes[] array
			for (j=0;j<treeObjs[i].childNodes.length;j++)
				if (treeObjs[i].childNodes[j].tagName == 'UL') {
					// if <ul>, call ul2tree()
					ul2tree(treeObjs[i].childNodes[j]);
				}
		}
}

 


Converting lists into trees

Going on tree.js, we?ll create now ul2tree(ulObj) function. As seen before, this function is called by getTrees() function.

An <ul> element is passed as argument to ul2tree(ulObj). For each <li> inside this <ul>, we check the existence of internal <ul>. If so, we set <li> class attribute to folder, we create a link to expand/collapse this item?s sub-tree, and we call, recursively, ul2tree(ulObj) function with the new <ul> as argument.

 

Text Box: function ul2tree(ulObj) {
	var liObj;
	var liCollapsed;
	// loop over all <ul> children using childNodes[] array
	for (var i=0;i<ulObj.childNodes.length;i++)
		// child tag = <li> ?
		if (ulObj.childNodes[i].tagName == 'LI') {
			liObj = ulObj.childNodes[i];
			// retrieve all <ul> inside <li>
			var ulObjs = liObj.getElementsByTagName('UL');
			// is there any <ul>?
			if (ulObjs.length) {
				liCollapsed = (liObj.className == 'collapsed');
				// set <li> class to 'folder'
				liObj.className = 'folder';
				// add a link to expand/collapse <li> sub-tree
				liObj.insertBefore(newExpandLink('[*]',liCollapsed),liObj.firstChild);
				// if <li> class = collapsed, hide internal <ul>
				if (liCollapsed) ulObjs[0].style.display = 'none';
				// call recursevely internal <ul> 
				ul2tree(ulObjs[0]);
			}
		}
	// set last <li> class to 'last'
	if (liObj.className == '') liObj.className = 'last'
	else liObj.className += ' last';
}

Further, we create two other functions:

Text Box: function newSpan(text) {
	// create an <span>...</span> element
	var span = document.createElement('SPAN');
	// create a test node from 'text' argument
	var spanText = document.createTextNode(text);
	// apppend text node to <span>
	span.appendChild(spanText);
	return span;
}

 

 

 

 

 

Text Box: function newExpandLink(text,collapsed) {
	// create an <a>...</a> element
	var expandLink = document.createElement('A');
	// create a <span>text</span> and add it to <a>
	expandLink.appendChild(newSpan(text));
	// set <a> class to 'minus' or 'plus'
	if (collapsed) expandLink.className='plus' 
	else expandLink.className='minus';
	return expandLink;
}

 


Implementing items expand/collapse functionality ? treeAction()

The link to expand/collapse items? sub-tree is just created. Now, we need to give it functionality through treeAction() function, which is called by onclick event at <div> class=tree.

This function recognizes which <a> element was clicked, modifying its class from minus to plus (or vice-versa), and hiding (or showing) the item?s sub-tree (<ul>).

Text Box: function treeAction(obj) {
	// retrieve clicked element
	if (document.all) obj = event.srcElement // IE
	else obj = obj.target;
	// if the element is not na <a>, return
	if (obj.tagName == 'SPAN') obj = obj.parentNode;
	if (obj.tagName != 'A') return;
	// change class
	if (obj.className == 'plus') obj.className = 'minus';
	else obj.className = 'plus';
	// get associated <li> 
	var liObj = obj.parentNode;
	var ulObjs = liObj.getElementsByTagName('UL');
	// if there is no internal <ul>, return
	if (!ulObjs.length) return;
	// hide/show internal  <ul>
	if ( ulObjs[0].style.display == 'none' ) ulObjs[0].style.display = 'block' 
	else ulObjs[0].style.display = 'none' ;
}

 


If we save tree.js file and refresh tree.htm page on browser, we should have:

 

 

Note that Administrative item is collapsed (as expected). Clicking on [*], we expand/collapse sub‑trees.

 

Layout via CSS

Our tree is able to expand/collapse sub-trees and tree items have appropriate classes.

We have, now, to show it as proposed in the beginning of this tutorial.

First, we will create tree.css file and call it from our page

 

As we write CSS rules, it will be shown how tree.htm page would look like on browser.

Codes that should be processed only by IE are preceded by ?//?.

Text Box: ...
<head>
	<title>Megaputz Inc.</title>
	<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
	<link rel="stylesheet" href="tree.css" type="text/css" title="default">
	<script language="JavaScript" type="text/javascript" src="tree.js"></script>
</head>
...

 


Defining general rules

All rules will refer to <div> class=tree as part of theirs selectors..

Inside tree.css file, we create two selectors: one for all elements (*) and another for all <ul>. Rules inside * selector define font properties and non-wrapping for internal text. Inside ul selector, the only rule hides list bullets.


 

CSS

IE

Firefox

div.tree * {

   font-family : "MS sans serif";

   font-size : 11px;

   margin : 0;

   padding : 0;

   white-space: nowrap;

}

 

div.tree ul {

   list-style : none;

}

 

 

Leveling spaces

We shall add another rule to ul selector and create a new selector to match all <li> elements. The new ul rule defines indentation (20px) and distance between an item and its sub-tree (2px). li rules define the box size around each item.

 

CSS

IE & Firefox

div.tree ul {

   list-style : none;

   padding : 2px 0 0 20px;

}

 

div.tree li {

   padding : 2px 0 2px 9px;

   line-height : 12px;

}

 

 <