Build an Object Oriented Tree Control In my last article, I demonstrated how to build an Explorer-like tree control using JavaScript. This month's installment will focus on converting the tree control to an object oriented structure. So, the problem is: Problem: How do I build an Explorer-like tree control that is easily extended using object oriented principles? Solution: Take advantage of JavaScript's polymorphic abilities and the solution is a snap. Polymorphism There are three fundamental principles of object oriented programming: inheritance, encapsulation, and polymorphism. I've discussed inheritance and encapsulation in a previous article [[Insert link here]]. This month's article will focus on the polymorphic aspects of JavaScript in the context of the tree control. Webopedia.com defines polymorphism like this: Generally, the ability to appear in many forms. In object-oriented programming, polymorphism refers to a programming language's ability to process objects differently depending on their data type or class. In truly object oriented languages, polymorpism is usually tied to class-based inheritance. That is, you define a class hierarchy with abstract classes at the top and concrete implementations further down. In the abstract class, you define a method that must be implemented or overridden in each subclass. The implementation of that method varies depending on the needs of the subclass. The classic example involves shapes. You might define an abstract class called Shape which includes a method called findArea(). You'd then define classes such as Rectangle and Circle that inherit from Shape. In each of the subclasses, you'd implement the findArea() method so that it returns the correct results based on the type of shape. Essentially, you'd just call findArea() on any shape without worrying about how the method does its work. JavaScript doesn't support class-based inheritance but it does include polymorphic abilities. In fact, JavaScript's prototype-based inheritance makes writing polymorphic methods simple and the structure closely mirrors that of a true object oriented language. Set up the HTML and CSS Open your favorite text editor and build the HTML and CSS for this project: <html> <head> <title>Object Oriented Tree</title> <style> body{ font: 10pt Verdana,sans-serif; color: navy; } .branch{ cursor: pointer; cursor: hand; display: block; } .leaf{ display: none; margin-left: 16px; } a{ text-decoration: none; } a:hover{ text-decoration: underline; } </style> </head> <body> </body> </html> Write the Script In any object oriented system, it's a good idea to create a hierarchy of objects that describes the system. In this implementation, I'll include a tree object that holds a collection of branch objects. Each branch object will, in turn hold a collection of children that can either be branches or leaf objects. In addition, each object type will implement a write() method that is polymorphic based on the individual object's needs. That is, a tree object will have a method called write() whose behavior is different than the branch object's write() method which, in turn, is different than the leaf object's write() method. I'll also include an add() method in the tree and branch objects that allows for the addition of children to the respective collection. Add a script block to the head of the document that includes the Image objects and functions necessary to achieve the effect: <script language="JavaScript"> var openImg = new Image(); openImg.src = "open.gif"; var closedImg = new Image(); closedImg.src = "closed.gif"; function showBranch(branch){ var objBranch = document.getElementById(branch).style; if(objBranch.display=="block") objBranch.display="none"; else objBranch.display="block"; swapFolder('I' + branch); } function swapFolder(img){ objImg = document.getElementById(img); if(objImg.src.indexOf('closed.gif')>-1) objImg.src = openImg.src; else objImg.src = closedImg.src; } </script> The script sets up the image objects necessary to show the user that a folder is either open or closed. The showBranch() and swapFolder() functions are event handlers for the document object that get fired after the tree has been written to the screen. For an explanation of these methods, please see my previous article [[Insert link here]]. Set up the Tree Object Add the tree object's constructor to the script: function tree(){ this.branches = new Array(); this.add = addBranch; this.write = writeTree; } The tree() constructor specifies that each tree object will include an array called branches that serves as the collection of children of the tree. In essence, the tree object represents the root node of the tree. The add and write properties are the polymorphic methods. They point to the following functions: function addBranch(branch){ this.branches[this.branches.length] = branch; } function writeTree(){ var treeString = ''; var numBranches = this.branches.length; for(var i=0;i<numBranches;i++) treeString += this.branches[i].write(); document.write(treeString); } The addBranch() method simply appends the object passed to the method onto the end of the branches array. The writeTree() method loops through all of the objects stored in the branches array and calls the write() method of each object. That's the beauty of polymorphism – I call the polymorphic write() method because each object in the branches array implements its own version of the write() method. Note that, because JavaScript's Array object allows you to store anything you'd like, in this implementation, you can only store objects that implement a write() method in the branches array. Set up the Branch Object The branch object is similar to the tree object: function branch(id, text){ this.id = id; this.text = text; this.write = writeBranch; this.add = addLeaf; this.leaves = new Array(); } The branch constructor includes id and text properties. The id property serves as the unique identifier for the document object written to the screen and the text property represents the text to display next to the folder. The leaves array is the collection of children to display for any branch node. Note that branch objects include the necessary write() method to enable their storage in the branches array of a tree object. By including write() and add() methods in both tree and branch objects, I've made those methods polymorphic. Here are the implementations for write() and add(): function addLeaf(leaf){ this.leaves[this.leaves.length] = leaf; } function writeBranch(){ var branchString = '<span class="branch" onClick="showBranch(\'' + this.id + '\')"'; branchString += '><img src="closed.gif" id="I' + this.id + '">' + this.text; branchString += '</span>'; branchString += '<span class="leaf" id="'; branchString += this.id + '">'; var numLeaves = this.leaves.length; for(var j=0;j<numLeaves;j++) branchString += this.leaves[j].write(); branchString += '</span>'; return branchString; } The addLeaf() function does the same thing as the addBranch() function of the tree object – it appends the object passed to the method to the end of the leaves collection. The writeBranch() method first sets up the HTML string necessary for the display of the branch and then loops through the leaves array and calls the write() method of each object stored in the array. Once again, you can only store objects that implement a write() method in the leaves array. Set up the Leaf Objects The leaf objects are actually the easiest objects in the system to set up: function leaf(text, link){ this.text = text; this.link = link; this.write = writeLeaf; } Each leaf object gets a text property for display and a link property. In addition leaf objects implement the write() method like this: function writeLeaf(){ var leafString = '<a href="' + this.link + '">'; leafString += '<img src="doc.gif" border="0">'; leafString += this.text; leafString += '</a><br>'; return leafString; } The writeLeaf function sets up the HTML string for display. Note that leaf objects don't need to implement an add() method as they represent the "end" of a branch. Build the Tree The only thing left to do is to build the tree on the page. The process is simple: create a tree object and add branches and/or leaves to it. Then add as many sub-branches and/or leaves as you'd like. When you've finished building the tree, call the tree object's write() method and the tree is written to the screen. Here's an example tree that is three levels deep. Add the following script between the body tags: <!-- place the tree building script where you'd like in the body --> <script language="JavaScript"> var myTree = new tree(); var branch1 = new branch('branch1','Branch 1'); var leaf1 = new leaf('Leaf 1','#'); var leaf2 = new leaf('Leaf 2','#'); branch1.add(leaf1); branch1.add(leaf2); var branch2 = new branch('branch2','Branch 2'); var leaf3 = new leaf('Leaf 3','#'); branch2.add(leaf3); branch1.add(branch2); myTree.add(branch1); var branch3 = new branch('branch3','Branch 3'); branch3.add(new leaf('Leaf 4','#')); branch2.add(branch3); var branch4 = new branch('branch4','Branch 4'); branch4.add(new leaf('Leaf 5','#')); branch1.add(branch4); var branch5 = new branch('branch5','Branch 5'); branch5.add(new leaf('Leaf 6','#')); myTree.add(branch5); myTree.add(new leaf('Leaf 7','#')); myTree.write(); </script> In the End Essentially, what I've created with the object oriented tree is a set of objects that implement what is usually called an interface in class-based inheritance languages. I've declared three objects (tree, branch, leaf) that implement the write interface. That is, they all include a write() method that supplies the behavior needed by the object based on its type. In addition, two of the objects (tree, branch) implement the add interface by supplying add() methods that work for their own respective object types. Polymorphism represents a powerful object oriented principle that allows for the creation of robust and scalabe systems. Using polymorphism allows me to separate the design of the objects from their implementation. In other words, I can say that trees, branches, and leaves should have a write() method (design) but that the way each object is written is different (implementation).