Introduction
The whole thing started with my exploring the AJAX and DHTML technologies. I guess a comparison of different toolkits available out there is a topic for a completely different article. Suffice to say that I was surprised by how little I new and how big of the world there is outside of the AJAX.NET. One problem, however, I encountered is how huge those JavaScript libraries are: most of the ones that do anything are in excess of 30,000 lines. That naturally brings up a maintenance question: if something goes bad, what do I do?
So, one of the first things I wanted to give a try was a tree control. It would seem to be simple. After all, HTML comes out of the box with UL/LI elements. So there would be very little I would need to do on top of the basic UL/LI HTML code:
- I would need to draw the node lines
- I would need to handle expand/collapse events
- I would need to show the selection
If I follow the outlined path, I should have very little JavaScript programming and virtually no conventions - just do the UL/LI combination.
<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>
new Tree("t");
</script>
Unfortunately, at the end, I ended up with a little more code than I anticipated, but the whole thing is still under 150 lines of HTML/CSS/JavaScript code - for all of us who have to stick around after the fun part of the initial development is over and the maintenance nightmare begins - sure a selling point.
Draw the node lines
I can't claim responsibility for most of this. Here is a link to the original article that got me started: http://odyniec.net/articles/turning-lists-into-trees/. The author explains in detail, step-by-step, on how to draw tree lines.
Internal design decisions
I decided to define a parent child relationship in the HTML code using the UL/LI combination. Internally, JavaScript respects this; however, some minor reformatting is done. The following things are prettied-up:
- An image icon is added as a first element for every
LI
. - A new
SPAN
element is created and all the LI
contents (except for any embedded sub-trees) are pushed down under this SPAN
element.
So after this reformatting, every LI
should have an IMG
that handles expand/collapse, a SPAN
with leaf contents that handles selection, and an optional UL
sub-tree. All this structure is initialized in the tree constructor, but the initChildren
function is provided if the tree/sub-tree contents are changed.
Inheritance
I decided not to do true inheritance. This would either fetch too much code or create dependency on the third-party library (that would fetch too much code). Instead, I did my customization through the JSON object you can pass to the constructor. Here is an example on how to change icons:
new Tree("t", {
icons : ["list.gif", "list.gif", "list.gif"]
});
If your project is using some OO JavaScript library, it would make sense to add inheritance and customize the default behaviors (expand, collapse, icon selection) in the derived class.