WebReference.com - Creating a Cross-Browser (DOM) Expandable Tree (4/5) | WebReference

WebReference.com - Creating a Cross-Browser (DOM) Expandable Tree (4/5)

To page 1To page 2To page 3current pageTo page 5
[previous] [next]

Creating a Cross-Browser (DOM) Expandable Tree

The next method of the jsTreeNode class is easily the most complicated one. We need the node to add itself to the page's document tree. Let's call this method addToDOM(). The addToDOM() method must not only add the calling node into the HTML page, but also must recursively add all child nodes. In this way, calling addToDOM() on the root node will lead to the entire tree being added into the document.

The only parameter to the addToDOM() method will be the element that will become the parent element of the calling node's HTML. The method will create a layer (<div>) to add to the parent element. Inside this layer will be drawn the node's HTML.

The HTML for a node is basically a table with four cells. The first cell appears blank, but provides the indent for each level of the tree; the second cell contains the plus/minus image used to expand the node's children; the third cell contains the icon for the node; the fourth cell contains the text.

The easiest way to accomplish this is to build up a string of HTML code and then set the layer's innerHTML attribute equal to the HTML string. Building up the string is done with the help of a string buffer called jsDocument (for more information about the string buffer and why it is used, please visit my Web site at http://www.nczonline.net).

The children of each node will be contained in another layer. This way, we can use the CSS display attribute to show and hide the children easily (remember, the CSS display attribute can effectively remove itself from an HTML page, removing the space that it previously took up, as opposed to using the visibility attribute, which only hides the layer and does not remove the space it takes up). We will then call the addToDOM() method on each child node, passing in this layer as the new parent element. The end result will be a series of nested layers that can be shown and hidden at certain points by setting the CSS display attribute to "none" (to hide) or "block" (to show).

The code for this method is as follows:

jsTreeNode.prototype.addToDOM = function (objDOMParent) {
 
    //create the URL 
    var strHTMLLink = "<a href=\"" + this.url + "\"";
    if (this.target)strHTMLLink += " target=\"" + this.target + "\">";
    
    //create the layer for the node 
    var objNodeDiv = document.createElement("div");
    
    //add it to the DOM parent element 
    objDOMParent.appendChild(objNodeDiv);
    
    //create string buffer 
    var d = new jsDocument;
    
    //begin the table 
    d.writeln("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>");
    
    //no indent needed for root or level under root 
    if (this.indent > 1) {
        d.write("<td width=\"");
        d.write(this.indent * INDENT_WIDTH);
        d.write("\"> </td>");
    }
    
    //there is no plus/minus image for the root 
    if (this.indent > 0) {
    
        d.write("<td width=\"18\" align=\"center\">");
        
        //if there are children, then add a plus/minus image 
        if (this.childNodes.length > 0) {
            d.write("<a href=\"javascript:objLocalTree.toggleExpand('");
            d.write(this.id);
            d.write("')\"><img src=\"");
            d.write(this.expanded ? imgMinus.src : imgPlus.src);
            d.write("\" border=\"0\" hspace=\"1\" id=\"");
            d.write("imgPM_" + this.id);
            d.write("\" /></a>");
        }
        
        d.write("</td>");
    }
    
    //finish by drawing the icon and text 
    d.write("<td width=\"22\">" +strHTMLLink + "<img hspace=\"1\" src=\"" + this.icon + "\" border=\"0\" align=\"absmiddle\" /></a></td>");
    d.write("<td nowrap=\"nowrap\">" + strHTMLLink + this.text + "</a></td>");
    d.writeln("</tr></table>");
        
    //assign the HTML to the layer 
    objNodeDiv.innerHTML = d;
    
    //create the layer for the children 
    var objChildNodesLayer = document.createElement("div");
    objChildNodesLayer.setAttribute("id", "divChildren_" + this.id);
    objChildNodesLayer.style.position = "relative";
    objChildNodesLayer.style.display = (this.expanded ? "block" : "none");
    objNodeDiv.appendChild(objChildNodesLayer);
    
    //call for all children 
    for (var i=0; i < this.childNodes.length; i++)
        this.childNodes[i].addToDOM(objChildNodesLayer);
}

Next, since a node can be expanded or collapsed, we will write a collapse() method and an expand() method to allow the developer to expand and collapse specific nodes. Each of these methods should check the current state of the node and throw an error if the wrong state is detected (i.e., you cannot collapse a node that is already collapsed, so throw an error). If the correct state is detected, each method will change the state, change the plus/minus image, and either show or hide the child nodes (note that we will get the layer to show/hide by using the getElementById() method). The code for these two methods is as follows:

jsTreeNode.prototype.collapse = function () {
 
    //check to see if the node is already collapsed 
    if (!this.expanded) {
    
        //throw an error 
        throw "Node is already collapsed"
 
    } else {
    
        //change the state of the node 
        this.expanded = false;
        
        //change the plus/minus image to be plus 
        document.images["imgPM_" + this.id].src = imgPlus.src;
        
        //hide the child nodes 
        document.getElementById("divChildren_" + this.id).style.display = "none";
    }
}
 
jsTreeNode.prototype.expand = function () {
 
    //check to see if the node is already expanded 
    if (this.expanded) {
    
        //throw an error 
        throw "Node is already expanded"
    
    } else {
    
        //change the state of the node 
        this.expanded = true;
        
        //change the plus/minus image to be minus 
        document.images["imgPM_" + this.id].src = imgMinus.src;
        
        //show the child nodes 
        document.getElementById("divChildren_" + this.id).style.display = "block";
    }
}

A quick note about using the throw command: I am simply throwing a string to the JavaScript interpreter, which will fire an uncaught exception notice in both IE5 and NS6. I could also have thrown an error object, as in the following:

throw new Error("Node is already expanded");

The reason I did not is that while NS6 supports the Error object, IE5 does not. IE began supporting it in version 5.5. Since I want this tree to work in IE starting in version 5.0, I cannot use the Error object. Of course, the irony is that if you tried to throw an Error object in IE5, it would cause an error anyway because the Error object would be undefined.


To page 1To page 2To page 3current pageTo page 5
[previous] [next]

Created: February 20, 2002
Revised: February 20, 2002

URL: http://webreference.com/programming/javascript/trees/4.html