How to Build a jQuery Treeview

Skill

How to Build a jQuery Treeview

Posted in:

Those of us who are used to GUI frameworks are used to the many components often built in. Everything from textboxes to progress bars are at our disposal. But when you move to the web there are no 'built in' version of many of these building blocks. The treeview has many incarnations on the web, and there is good reason. It is a versatile component, usable in any type of collection management. There are many ways to get a treeview going on the web, but today we will be taking jQuery and making a quick and dirty treeview - one that can be built and used in a matter of minutes.

So, to start with, we are going to need some data. Our treeview is going to take an array of data and build an HTML treeview from it. I prefer JSON, but you can use traditional XML if you want, either way will work. Our data looks something like this:

var data = {
  'title': "SOTC Treeview",
  'items': {
    "Item 1": "SOTC is cool",
    "Item 2": "SOTC is Aweseome",
    "Item 3": "SOTC is the best!",
    "Item 4": {
      'title': "Sub Menu 1",
      'items': {
        "Sub Item 1": "SOTC is still awesome!",
        "Sub Item 2": "SOTC is still cool!",
        "Sub Item 3": "SOTC is still the best!"
      }
    }
  }
};

Notice that we have a title and items, and basically take this structure and repeat it in our sub menu. With the treeview builder we are going to create, you can have as many submenus you want, as long as you keep that basic structure.

For our treeview, we are going to make a js object, which we can add to or update later on. To start, we are gong to make the basic object:

treeview = function(tData, container)
{  
  this.tree = this.build(tData);
  var treeCon = container;
 
  treeCon.append(this.tree);
}

So what we are going to do construction wise is take in the data and a container to put the finished treeview in. You can just take in the data and set the tree property if you want, but I prefer the object constructor to do that for me. This way we have a reference to the tree's container, just in case we need it.

Now, as of right now we are calling a build method that is missing. Of course this is the meat of the whole treeview object, so let's get to building the build method.

This build method is not terribly complex or large, but there is no real good way to break it into parts, so here it is:

this.build = function(nodeInfo)
{
  var nodeID = nodeInfo['title'].replace(/\s/g, "_");
  var node = $('<div id="'+nodeID+'-Node">'+nodeInfo['title']+'</div>')
    .css({'margin-top': 5});
 
  $('<div class="expandNode expand"></div>')
    .prependTo(node);
     
  var contents = $('<div class="NodeContents"></div>');
 
  for(item in nodeInfo['items'])
  {
    if(typeof nodeInfo['items'][item] == 'string' ||
       typeof nodeInfo['items'][item] == 'number')
    {
      $('<div class="NodeItem"></div>')
        .html('<span class="ItemTitle">'+item+'</span><div class="ItemTxt">'+nodeInfo['items'][item]+'</div>')
        .appendTo(contents);
    }
    if(typeof nodeInfo['items'][item] == 'object')
    {
      sNode = this.build(nodeInfo['items'][item]);
      contents.append(sNode);
    }
  }
 
  node.append(contents);
 
  node.children('.expandNode').click(function() {
    var contents = $(this).parent().children(".NodeContents");
    contents.toggle();
    if(contents.css('display') != "none")
    {
      $(this).attr("class", "expandNode collapse");
    }
    else
    {
      $(this).attr("class", "expandNode expand");
    }
   
  });
 
  return node;
}

The very first thing we do is take the node title and replace all spaces with underscores. This way there are no spaces in the element id. Then, we have to make the base node div. Simple enough, and we give it a 5px margin, so all sub nodes are offset. Then we add the expand node div, which is what you click to expand the node. After that we get into the real crazy parts. Of course the node contents div is not crazy, but the items that go inside it are. For each item in the items array, we first test the type of the item. If it is a string or number, take it and make it into a node item. In this case, we are taking the item's id from the array and using it as the title. But you can simply list the item if you want. That is the great thing about this treeview, it is really simple to modify it however you want.

Now, if the item happens to be an object, we assume it is a new node and build it. This recursive behavior allows us to add as many nodes and sub nodes as we want. It will just keep building until it can't anymore. After all the items are built, we need to add the functionality. With jQuery this is incredibly easy. We first hide the node contents and add it to the node base div. Finally, we take the expand div and add a click event. This click event takes the expand button's parent (the node base div), then grabs its children, and selects the "NodeContents" div from this list of children. This finds the buttons related content, no matter what sub node it is in. This is because the find is relative to itself, using jQuery's awesome selectors. Once we are done with all that, we return the node object.

All we have to do is add this function to the Treeview object. We are going to add it above the construct code, so we can access this function. The final object will look something like so:

treeview = function(tData, container)
{
  this.build = function(nodeInfo)
  {
    var nodeID = nodeInfo['title'].replace(/\s/g, "_");
    var node = $('<div id="'+nodeID+'-Node">'+nodeInfo['title']+'</div>')
      .css({'margin-top': 5});
   
    $('<div class="expandNode expand"></div>')
      .prependTo(node);
       
    var contents = $('<div class="NodeContents"></div>');
   
    for(item in nodeInfo['items'])
    {
      if(typeof nodeInfo['items'][item] == 'string' ||
         typeof nodeInfo['items'][item] == 'number')
      {
        $('<div class="NodeItem"></div>')
          .html('<span class="ItemTitle">'+item+'</span>'+
           '<div class="ItemTxt">'+nodeInfo['items'][item]+'</div>')
          .appendTo(contents);
      }
      if(typeof nodeInfo['items'][item] == 'object')
      {
        sNode = this.build(nodeInfo['items'][item]);
        contents.append(sNode);
      }
    }
   
    node.append(contents);
   
    node.children('.expandNode').click(function() {
      var contents = $(this).parent().children(".NodeContents");
      contents.toggle();
      if(contents.css('display') != "none")
      {
        $(this).attr("class", "expandNode collapse");
      }
      else
      {
        $(this).attr("class", "expandNode expand");
      }
     
    });
   
    return node;
  }
 
  this.tree = this.build(tData);
  var treeCon = container;
 
  treeCon.append(this.tree);
}

All this javascript is fun and neat, but it accomplishes little without its CSS counterpart. The CSS is actually pretty straight forward and simple in this case:

#JQTreeview
{
  border: 1px solid #11B700;
  padding: 5px;
}

.expandNode
{
  cursor: pointer;
  width: 16px;
  height: 16px;
  background-color: #000;
  float: left;
  margin-right: 8px;
}

.expand
{
  background: url(images/expandIcon.png) no-repeat;
}

.collapse
{
  background: url(images/collapseIcon.png) no-repeat;
}

.NodeContents
{
  border-left: 6px solid #D2FFCC;
  margin-left: 4px;
  display: none;
  padding-left: 5px;
}

span.ItemTitle
{
  font-size: 10px;
  width: 100px;
  text-decoration: underline;
}

div.NodeItem div.ItemTxt
{
  font-size: 14px;
  padding: 2px 5px;
  margin-bottom: 2px;
  text-decoration: none;
}

Pretty straight forward CSS. The last thing to do is use it all:

$(document).ready(function() {
         
  var data = {
    'title': "SOTC Treeview",
    'items': {
      "Item 1": "SOTC is cool",
      "Item 2": "SOTC is Aweseome",
      "Item 3": "SOTC is the best!",
      "Item 4": {
        'title': "Sub Menu 1",
        'items': {
          "Sub Item 1": "SOTC is still awesome!",
          "Sub Item 2": "SOTC is still cool!",
          "Sub Item 3": "SOTC is still the best!"
        }
      }
    }
  };

  tView = new treeview(data, $('body'));
});

This code, of course, goes somewhere in your HTML page, and you can pass it any element reference you want. This type of treeview is good for data driven treeviews, and is not the best, but it is easy to build and quick to get going. All you need is some data and somewhere to put the treeview.

This is about it for this tutorial. I hope this quick and simple treeview will provide something to get you going. jQuery has a lot to offer, so I suggest checking out our other jQuery tutorials. Just remember, when you need programming help or advice, all you have to do is Switch On The Code.

The Reddest
06/11/2009 - 08:53

An HTML id is supposed to be unique for an entire document. This code is creating multiple elements with the id "expandNode" and "NodeContents".

reply

The Hairiest
06/11/2009 - 09:32

Code has been fixed to use classes rather than non-unique IDs.

reply

The Reddest
06/11/2009 - 10:46

That was fast. Sweet.

reply

Anonymous
02/25/2010 - 12:51

Gracias.

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.

Sponsors