Eden Ridgway's Blog

.Net and Web Development Information

  Home :: Contact :: Syndication  :: Login
  105 Posts :: 1 Stories :: 78 Comments :: 3 Trackbacks

Search

Article Categories

Archives

Post Categories

Development

General

/SubText/Imagesach is a reaction to this type of markup. Ultimately one should use markup that reflects the type of information being presented. Hence you have CSS menus that uses nested ordered/unordered lists since this HTML more accurately represents the type of information being displayed. The same is true for treeviews, they are just nested lists of data and if one applied a little thought to the problem, it is quite easy to create a CSS based treeview solution.

I'm going to present a simple example of a CSS based treeview I recently wrote. The markup below represents a two level treeview with a single nested list:

<!-- Level 1 nodes -->
<ul class="TreeView" id="TreeView">
<li class="Collapsed"> First level 1 node
<!-- level 2 nodes -->
<ul>
<li>First level 2 node</li>
<li>Second level 2 node</li>
</ul>
</li>
</ul>

Without any CSS styling the information looks like this:

Now by default I want the first level node to be collapsed with a plus image next to it, to indicate that the node can be expanded. In order to achieve this we can get rid of the li symbols by making the list items float left. This makes them stack up, so you can then set the elements to display at a 100% of the containing element. We then want space to place our treeview expandable image, so we put in a left padding of 18px. To make life simpler you can use a CSS child selector to apply this to all list elements within the treeview, like this:

.TreeView LI
{
padding
: 0 0 0 18px;
float
: left;
width
: 100%;
list-style
: none;
}

Adding the collpasing and expanding images and the associated effects to the child nodes is achieved with the following styles:

LI.Expanded
{
background
: url(minus.gif) no-repeat left top;
}

LI
.Expanded ul
{
display
: block;
}

LI
.Collapsed
{
background
: url(plus.gif) no-repeat left top;
}

LI
.Collapsed ul
{
display
: none;
}

Then one needs to add JavaScript to the page to toggle the applied styles. For this, I decided to go with an approach that could handle multiple styles being applied to a single element, so my solution is a little more complex than a simple className replacement approach:

Array.prototype.indexOf = IndexOf;

//Toggles between two classes for an element
function ToggleClass(element, firstClass, secondClass, event)
{
event.cancelBubble = true;

var
classes = element.className.split(" ");
var
firstClassIndex = classes.indexOf(firstClass);
var
secondClassIndex = classes.indexOf(secondClass);

if
(firstClassIndex == -1 && secondClassIndex == -1)
{
classes[classes.length]
= firstClass;
}
else if (firstClassIndex != -1)
{
classes[firstClassIndex]
= secondClass;
}
else
{
classes[secondClassIndex]
= firstClass;
}

element.className
= classes.join(" ");

}

//Finds the index of an item in an array
function IndexOf(item)
{
for (var i=0; i < this.length; i++)
{
if (this[i] == item)
{
return i;
}
}

return -1;
}

I then wrote a script to setup the treeview based on a nested list container. The main reason I chose not to have all the event wiring in the HTML itself is that my approach leads to cleaner markup and allows one to easily convert any unordered list into a treeview. Here is the script to hookup the click events (it works in both IE and FireFox):

//The toggle event handler for each expandable/collapsable node
//- Note that this also exists to prevent any IE memory leaks
//(due to circular references caused by this)
function ToggleNodeStateHandler(event)
{
ToggleClass(
this, "Collapsed", "Expanded", (event == null) ? window.event : event);
}

//Prevents the onclick event from bubbling up to parent elements
function PreventBubbleHandler(event)
{
if (!event) event = window.event;
event
.cancelBubble = true;
}

//Adds the relevant onclick handlers for the nodes in the tree view
function SetupTreeView(elementId)
{
var tree = document.getElementById(elementId);
var
treeElements = tree.getElementsByTagName("li");

for
(var i=0; i < treeElements.length; i++)
{
if (treeElements[i].getElementsByTagName("ul").length > 0)
{
treeElements[i].onclick
= ToggleNodeStateHandler;
}
else
{
treeElements[i].onclick
= PreventBubbleHandler;
}
}
}

In this example to get it all going, you would call SetupTreeView("TreeView") after the page has loaded to setup the treeview for the nested lists.

And this is the end result: CssTreeView.htm

I was informed of a TreeView control that pretty much does what I blogged about here and more. You should take a look at the SilverStripe Tree Control if you found this post interesting. Thanks Garreth for the information.

posted on Sunday, October 30, 2005 9:48 AM
Comments have been closed on this topic.