Click here to Skip to main content
15,895,084 members
Articles / Programming Languages / Javascript

Tiny JavaScript tree

Rate me:
Please Sign up or sign in to vote.
4.69/5 (10 votes)
21 Nov 2007CPOL2 min read 47.4K   633   34  
A tiny Javascript tree.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Untitled Page</title>
    <style type="text/css">
		/* style sheets for the tree. Idealy need to be extracted and moved to the separate file
		   along with javascript code below. */
		   
		/* style sheet for a tree object UL element and any sub-elements */
		/* NOTE: this style will be automatically assigned by the javascript */
		ul.__tree__, ul.__tree__ ul {
			background: url(vline.png) repeat-y;	/* vertical line repeated vertically to fill up any gaps */
			margin: 0 0 0 10px;						/* changes default UL margins and padding */
			padding: 0;
		}
		/* style sheet for the each tree item */
		ul.__tree__ li {					
			background: url(node.png) no-repeat;	/* |- image to prefix each node */
			list-style: none;						/* do not display any default LI markings */
			padding: 0 12px;						/* offset LI */
		}
		ul.__tree__ li.__last_child__ {				/* |_ image for the last child */
			background: #fff url(lastnode.png) no-repeat;
		}
		ul.__tree__ span.__selected__ {				/* show text for the selected node in different color */
			background: blue !important;
			color: white;
			
		}
    </style>
</head>
<body>
<!-- tree contents: the tree and all the subnodes are under UL tag. 
	 Leaf elements ar LI items. If there are subnotes, they go into the sub-UL -->
<ul id="t">
	<li>Item1</li>
	<li>Item2
		<ul>
			<li>Item2.1</li>
			<li><a onclick="alert('look mom, I\'m here!');">Item2.2</a>
				<ul>
					<li>Item2.2.1</li>	
				</ul>
			</li>
			<li>Item2.3</li>
		</ul>
	</li>
	<li>Item3</li>
	<li>Item1</li>
</ul>

<script type="text/javascript">
/* Tree class, pass ID of the UL element to attach itself to, and customization functions 
	the example of calling with customizations would be:
		new Tree('someUl', { getIcon : function(li, tree) { return "some.gif" } });
	I guess one of the things to do would be to implement a true inheritance, but then it would be 
	extra code. If your project utilizes any kind of javascript libraries, then you can do true 
	inheritance using whatever means they provide. As is this is independent of any OO implementation
*/
function Tree(elId, opt) {
	this.elem = document.getElementById(elId);				// save main UL element
	this.elem.className += " __tree__ ";					// force style sheet for the whole tree.
	this.selected = null;									// currently selected LI element.
	
	this.defOpts = {										// default implementation of the config options
		icons : [ "list.gif", "fold.gif", "open.gif" ],
		getIcon : function(li, tree) {						// getIcon - gets called during the initialization or any
			if (opt && opt.getIcon)							// time image is clicked.
				return opt.getIcon(li, tree);				// if alternative implementation provided by caller/user - use it.
			var uls = li.getElementsByTagName("ul");		// see if there are any child nodes.
			var icons = (opt && opt.icons) ? opt.icons : this.icons;
			if (uls.length == 0)							
				return icons[0]						// return default icons
			return icons[uls[0].style.display == "none" ? 1 : 2];
		},
		onClickImage : function(li, tree) {					// gets called when image is being clicked to 
			if (opt && opt.onClickImage)					// expand / collapse node
				return opt.onClickImage(li, tree);			// use new implementation if provided.
			var uls = li.getElementsByTagName("ul");		// check if there are sub-nodes
			if (uls.length == 0)
				return;										// toggle visibility of the child UL
			uls[0].style.display = (uls[0].style.display != "none") ? "none" : "block";
			var imgs = li.getElementsByTagName("img");		// our image is the first one.
			imgs[0].src = this.getIcon(li);
		}
	};
	
	this.initItem = function(li) {							// initializes LI element - tree leaf
		li.tree = this;										// save reference to the tree object
		if (li.initDone)									// if already initialized - get out
			return;
		li.initDone = true;
		var img = document.createElement("img");			// create image element
		img.src = this.defOpts.getIcon(li, this);			// assign image URL
		img.onclick = function(e) {							// handle on-expand/collapse clicks on image
			e = e || window.event; 
			var t = e.target || e.srcElement;				// find element - image. It's direct parent should be LI
			t.parentNode.tree.defOpts.onClickImage(t.parentNode, this);		// call handler to process event.
		}
		var span = document.createElement("span");			// create span - our LI should have up to 3 nodes in
		for (var i = li.childNodes.length - 1; i >= 0; i--) // image, span with leaf contents and possible UL - sub-tree
			if (li.childNodes[i].nodeName == "UL")			// if sub-tree - initialize it too
				this.initChildren(li.childNodes[i])
			else											// move everything else inside the span
				span.insertBefore(li.removeChild(li.childNodes[i]), span.firstChild);
		li.insertBefore(span, li.firstChild);				// insert span/leaf
		li.insertBefore(img, li.firstChild);				// insert image
		
		span.onclick = function(e) {						// handle highligh action.
			e = e || window.event; 
			var li = e.target || e.srcElement;				// find LI/leaf that generated the event
			while (li && li.nodeName != "LI")
				li = li.parentNode;
			if (!li)
				return;
			if (li.tree.selected)							// un-select previous selection
				li.tree.selected.className = li.tree.selected.className.replace(", __selected__ ,", "");
			var span = li.firstChild.nextSibling;			// find span within LI
			span.className += ", __selected__ ,";			// set style and save selection
			li.tree.selected = span;
		}
	};
	
	this.initChildren = function(ul) {						// initialize tree/sub-tree UL elements
		var last = true;
		for (var li = ul.lastChild; li; li = li.previousSibling)
			if (li.nodeName == "LI") {
				this.initItem(li);
				if (last) {
					li.className += ", __last_child__ ,";	// set class name for the last element |_
					last = false;
				}
			}
	}
	
	this.initChildren(this.elem);
	return this;
}


// do the magic!!!
new Tree("t");
		
		
</script>
</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
http://www.GaspMobileGames.com
United States United States
Writing code since 1987 using whatever language/environment you can imagine. Recently got into the mobile games. Feel free to check them out at http://www.GaspMobileGames.com

Comments and Discussions