devxlogo

Build an Object-Oriented Tree Control Using JavaScript

Build an Object-Oriented Tree Control Using JavaScript

n my last article, I demonstrated how to build an Explorer-like tree control using JavaScript. This month’s installment focuses on converting the tree control to an object-oriented structure. Such a tree control should be both reusable and easily extended.



How do I build an Explorer-like tree control that is easily extended using object-oriented principles?



Take advantage of JavaScript’s polymorphic abilities and the solution is a snap.There are three fundamental principles of object-oriented programming: inheritance, encapsulation, and polymorphism. I’ve discussed inheritance and encapsulation in a previous article entitled Encapsulate Your JavaScript: Keep Private Methods Private. This month’s article focuses on the polymorphic aspects of JavaScript in the context of the tree control.

Here’s how Webopedia.com defines polymorphism:

“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, polymorphism 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 subclasses must implement or override. The implementation of that method varies depending on the needs of the subclass.

The classic example involves shapes. For example, the formula to find the area of a rectangle is completely different from the formula to find the area of a circle. You might define an abstract class called Shape that includes a method called findArea() that subclasses must implement. 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. The end result is that you can just call the findArea() method 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 makes your program structure more closely mirror that of a true object-oriented language.Setting up the HTML and CSSOpen your favorite text editor and enter or copy the HTML code and CSS styles for this project:

Object-oriented Tree

Write the Script
In any object-oriented system, it’s a good idea to create a hierarchy of objects that describes the system. This implementation includes a tree object that holds a collection of branch objects. Each branch object, in turn, holds a collection of children that can be either branches or leaf objects. In addition, each object type implements a polymorphic write() method that acts appropriately for that object’s needs. That is, a tree object has a write() method whose behavior differs from a branch object’s write() methodwhich is different from the leaf object’s write() method. The tree and branch objects also expose add() methods that lets you add children to their respective collections.

Add a script block to the head of the document that creates the Image objects and functions necessary to achieve the effect of expanding and collapsing the branches of the tree, displaying or hiding the child items for any branch, and swapping the open or closed folder image as the branches exapand or collapse:

The preceding 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 rendered to the screen. For an explanation of these methods, please see my previous article, entitled Build a JavaScript Tree Control.


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 object represents the root node of the tree. The tree() constructor guarantees that each tree object has an array called branches that serves as the collection of children of the tree. . The add and write properties are the polymorphic methods, implemented as pointers 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 

The addBranch() method 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; you can call the polymorphic write() method because each object in the branches array implements its own appropriate version of the write() method. Note that, although JavaScript's Array object lets you store anything you'd like in an array, in this implementation you can store only objects that implement a write() method in the branches array. Unlike other more strongly typed languages, JavaScript won't enforce that rule; you need to enforce it yourself.


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 a collection of children the control displays 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. Because both the tree and branch objects include write() and add() methods, those methods are polymorphic. Here are the implementations for write() and add():

function addLeaf(leaf){   this.leaves[this.leaves.length] = leaf;}function writeBranch(){   var branchString =       '' + this.text;   branchString += '';   branchString += '';   var numLeaves = this.leaves.length;   for (var j=0;j

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 = '';   leafString += '';   leafString += this.text;   leafString += '
'; 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 to display it on the screen. Here's an example tree that's three levels deep. Add the following script between the body tags in an HTML page:

Essentially, the object-oriented tree is a set of objects that implement what class-based inheritance languages usually term an interface. There are three objects (tree, branch, leaf) that implement the write interfacethat 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 scalable systems. Using polymorphism lets you separate object design from object implementation. In other words, you can say that trees, branches, and leaves should each have a write() method (design) but that each is written differently (implementation).


devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist