Web Treeview Menu Java Script

This type of simple program can be found on a large number of web sites, aid visitors find the information they are looking for quickly and easily. But how do they work, the answer is simple they build the Treeview structure with a number of images and text using HTML according to the data held in the program. When certain images are selected instead of opening a URL they execute a function of the program, this then changes one of the data value held within the program and then redisplays the new structure. Other parts of the Treeview are set-up to open the URLs in the program's display Frame.

The first part of using the Treeview is setting up your index page for the Treeview to do this you will need to add the following two tags into your header, the first loads the Treeview, the second sets up your menu.

<SCRIPT LANGUAGE="JavaScript1.2" SRC="TreeView.js"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript1.2" SRC="TreeData.js"></SCRIPT>

The only thing that TreeData.js contains is the loadTreeData() function that sets the treeview menu up, and looks like this.

  function loadTreeData()
  {
    TreeViewData.add(new Node('root', '', '', '', -1, 'Root node', 'home.html', '', 16, 16));
    TreeViewData.add(new Node('folder1', 'root', '', '', 0, 'Folder with URL', 'disclaim.html', '', 16, 16));
    TreeViewData.add(new Node('folder2', 'folder1', '', '', 0, 'Folder without URL', '', '', 16, 16));
    TreeViewData.add(new Node('link1', 'folder2', '', '', -1, 'Link in folder2 with URL', 'http://www.thetoxiczone.com/', '_top', 16, 16));
    TreeViewData.add(new Node('link2', 'folder1', '', '', -1, 'Link in folder2 without URL', '', '', 16, 16));
  }

The fields of each node from left to right are name, parent, closed icon, open icon, initial state, display name, URL, Target Frame, iconwidth and iconheight. The name should be unique, and the parent should be the name of the parent folder you wish to place the node. The closed and open icons can be left blank, or if you wish to use a different icon to the default the file name can be used here. The initial state of the Node can be one of three thing a link -1, a closed folder 0 or an open folder 1. The display name is the text that is used for the link if you supply a URL, the Target Frame is the name of the Frame that the link will open in finally the icon size this is 16 x 16 for the default but if you are using a icon that is bigger or smaller then you can change it to a new size.

Next thing to do is replace the <BODY></BODY> section of you page with the following, this will set-up the two frames needed for the treeview to work. When this is loaded into a borrowers it instructs the browser to execute the Java script functions InitTreeView(); loadTreeData(); drawTreeView();, this sets up the treeview, then loads the treeview data, then finally draws the treeview by replacing the contents of the navFrame with the treeview HTML.

<FRAMESET COLS="197,*" FRAMEBORDER="NO" BORDER="0" FRAMESPACING="0" ROWS="*" ONLOAD="InitTreeView();loadTreeData();drawTreeView();">
<FRAME NAME="navFrame" SRC="navframe.html" SCROLLING="AUTO">
<FRAME NAME="mainFrame" SRC="home.html" SCROLLING="AUTO">
</FRAMESET>
<NOFRAMES>
<BODY BGCOLOR="#FFFFFF">
</BODY>
</NOFRAMES>

Now to explain the workings of TreeView.js contained it the Treeview.zip download.

The first five lines of this set the colours of the treeview frame, next is the background image, in this case no image, next comes the fonts and font size to be used. The next few line set the images to use in the treeview, starting with size of the line images followed by the relative path for all the images, next is the default root icon, link icon, open and closed folder icons, these are followed by the icons that make up the lines of the treeview. The oneBranch determines if only one branch of the treeview is open at one time. Then come the references to the html files to display above and below the treeview along with the height to make the top and bottom in-line frames, note that Netscape can't handle this. Then we need to know if we are running with IE or Netscape for making the small change between the two browsers. Finally come the default target frame and the setting up of the Collection that stores the Nodes of the treeview.

function InitTreeView()
{
    bgColour = '#FFFFFF';
    lColour = '#0000AA';
    alColour = '#FF0000';
    vlColour = '#880088';
    tColour = '#000000';
    bgImage = '';
    TreeViewFont = 'MS Sans Serif,Arial,Helvetica'
    TreeViewFontSize = '10pt';
    lineWidth = 19;
    lineHeight = 16;
    imageRoot = 'images/';
    defaultRoot = 'tv-root.gif';
    defaultLink = 'tv-link.gif';
    defaultFolderOpen = 'tv-folder-open.gif';
    defaultFolderClosed = 'tv-folder-closed.gif';
    linkCont = 'tv-link-cont.gif';
    linkEnd = 'tv-link-end.gif';
    closedCont = 'tv-closed-cont.gif';
    closedEnd = 'tv-closed-end.gif';
    openCont = 'tv-open-cont.gif';
    openEnd = 'tv-open-end.gif';
    vline = 'tv-line.gif';
    blank = 'tv-blank.gif';
    oneBranch = false;
    topHTML = '';
    topHeight = 10;
    bottomHTML ='bottomframe.html';
    bottomHeight = 100;
    defaultTarget = 'mainFrame';
    iExplorer = (navigator.appName.indexOf("Internet Explorer") != -1) ;
    TreeViewData = new Collection();
    return;
}

The drawTreeView function start the process of creating the treeview, it begins by opening the navFrame as a text/html document, and creates all the cascade style sheet and BODY settings, then if a reference has been set to a top html page it create the top frame. The next step is to draw the root node of the treeview if the data contains any node, followed by all the root nodes sub nodes. The if a reference as been set to a bottom html page it create the bottom frame, followed by the closing html statements. This function contains the fixes for the Netscape and IE differences.

Function drawTreeView()
{
    TreeViewOutput = self.navFrame.window.document;
    TreeViewOutput.open("text/html");
    TreeViewOutput.write("<HTML><TITLE>NavFrame</TITLE>\n");
    TreeViewOutput.write("<style type=\"text/css\">\n");
    TreeViewOutput.write("<!--\n");
    TreeViewOutput.write("p { font-family: " + TreeViewFont + ";\n");
    TreeViewOutput.write("font-size: " + TreeViewFontSize + ";\n");
    TreeViewOutput.write("color: " + tColour + ";\n");
    TreeViewOutput.write("background-color: " + bgColour + " }\n");
    TreeViewOutput.write("a { color: " + lColour + " }\n");
    TreeViewOutput.write("a:active { color: " + alColour + " }\n");
    TreeViewOutput.write("a:visited { color: " + vlColour + " }\n");
    TreeViewOutput.write("-->\n");
    TreeViewOutput.write("</style></HEAD>\n");
    TreeViewOutput.write("<BODY BGCOLOR='" + bgColour + "' BACKGROUND='" + bgImage + "'>\n");

    if (topHTML != "")
    {
        if (iExplorer)
        {
            TreeViewOutput.write("<IFRAME BORDER = \"0\" MARGINWIDTH = \"0\" MARGINHEIGHT = \"0\" HSPACE = \"0\" VSPACE = \"0\" FRAMEBORDER = \"0\" FRAMESPACING = \"0\" SCROLLING=\"NO\" WIDTH=\"100%\" HEIGHT=\"" + topHeight + "\" SRC='" + topHTML +"'>");
             TreeViewOutput.write("</IFRAME>");
        }
    }
    TreeViewOutput.write("<P>");
    if (TreeViewData.length > 0)
    {
        TreeViewOutput.write("<IMG SRC='" + imageRoot + TreeViewData[1].iconClosed + "' WIDTH =" + TreeViewData[1].iconWidth + " HEIGHT=" + TreeViewData[1].iconHeight + "' ALIGN=TEXTTOP BORDER=0>&nbsp");
        if (TreeViewData[1].url != '')
            TreeViewOutput.write("<A HREF='" + TreeViewData[1].url + "' TARGET='" + TreeViewData[1].target + "' onMouseOver=\"window.status='" + TreeViewData[1].url + "'; return true\" onMouseOut=\"window.status=''; return true\"><B>" + TreeViewData[1].name + "</B></A><BR>\n");
        else
            TreeViewOutput.write("<B>" + TreeViewData[1]Name + "</B>\n");
    drawBranchNodes("root","");
    }
    TreeViewOutput.write("</P>");
    if (bottomHTML != "")
    {
        if (iExplorer)
        {
            TreeViewOutput.write("<IFRAME BORDER = \"0\" MARGINWIDTH = \"0\" MARGINHEIGHT = \"0\" HSPACE = \"0\" VSPACE = \"0\" FRAMEBORDER = \"0\" FRAMESPACING = \"0\" SCROLLING=\"NO\" WIDTH=\"100%\" HEIGHT=\"" + bottomHeight + "\" SRC='" + bottomHTML +"'>");
            TreeViewOutput.write("</IFRAME>");
        }
    }
    TreeViewOutput.write("</BODY>\n");
    TreeViewOutput.write("</HTML>");
    TreeViewOutput.close();
    return;
}

The drawBranchNodes function is the centre of the treeview, it draws all the nodes and recursively draws all the open folder nodes. It does this by getting all the child nodes from the supplied parent node, then for each child node in sequence it draws the supplied image string that may be a sequence of spacer icons and vertical lines followed by the relevant linking line icon depending whether it is an end of list or mid-list node.

Function drawBranchNodes(parentNode, imageString)
{
    var childnodes = extractChildNodes(parentNode);
    var currentNode = 0;
    while (currentNode < childnodes.length)
    {
        var newImageString = imageString;
        currentNode++;
        TreeViewOutput.write(imageString);
        if (currentNode != childnodes.length)
            switch (childnodes[currentNode].state)
            {
            case -1:
                TreeViewOutput.write("<IMG SRC='" + imageRoot + linkCont + "' WIDTH =" + lineWidth + " HEIGHT=" + lineHeight + " ALIGN=TEXTTOP>");
                break;
            case 0:
                TreeViewOutput.write("<IMG SRC='" + imageRoot + closedCont + "' WIDTH =" + lineWidth + " HEIGHT=" + lineHeight + " ALT='[Expand]' ALIGN=TEXTTOP BORDER=0 onClick=\"self.parent.toggle('" + childnodes[currentNode].id + "'); return true\" onMouseOver=\"window.status='[Expand]'; return true\" onMouseOut=\"window.status=''; return true\"></A>");
                break;
            default:
                TreeViewOutput.write("<IMG SRC='" + imageRoot + openCont + "' WIDTH =" + lineWidth + " HEIGHT=" + lineHeight + " ALT='[Collapse]' ALIGN=TEXTTOP BORDER=0 onClick=\"self.parent.toggle('" + childnodes[currentNode].id + "'); return true\" onMouseOver=\"window.status='[Collapse]'; return true\" onMouseOut=\"window.status=''; return true\"></A>");
                newImageString = newImageString + "<IMG SRC='" + imageRoot + vline + "' WIDTH =" + lineWidth + " HEIGHT=" + lineHeight + " ALIGN=TEXTTOP>";
        }
        else
            switch (childnodes[currentNode].state)
            {
            case -1:
                TreeViewOutput.write("<IMG SRC='" + imageRoot + linkEnd + "' WIDTH =" + lineWidth + " HEIGHT=" + lineHeight + " ALIGN=TEXTTOP>");
                break;
            case 0:
                TreeViewOutput.write("<IMG SRC='" + imageRoot + closedEnd + "' WIDTH =" + lineWidth + " HEIGHT=" + lineHeight + " ALT='[Expand]' ALIGN=TEXTTOP BORDER=0 onClick=\"self.parent.toggle('" + childnodes[currentNode].id + "'); return true\" onMouseOver=\"window.status='[Expand]'; return true\" onMouseOut=\"window.status=''; return true\">");
                break;
            default:
                TreeViewOutput.write("<IMG SRC='" + imageRoot + openEnd + "' WIDTH =" + lineWidth + " HEIGHT=" + lineHeight + " ALT='[Collapse]' ALIGN=TEXTTOP BORDER=0 onClick=\"self.parent.toggle('" + childnodes[currentNode].id + "'); return true\" onMouseOver=\"window.status='[Collapse]'; return true\" onMouseOut=\"window.status=''; return true\">");
                newImageString = newImageString + "<IMG SRC='" + imageRoot + blank + "' WIDTH =" + lineWidth + " HEIGHT=" + lineHeight + " ALIGN=TEXTTOP>";
            }
        if (childnodes[currentNode].state == 1)
            TreeViewOutput.write("<IMG SRC='" + imageRoot + childnodes[currentNode].iconOpen + "' WIDTH =" + childnodes[currentNode].iconWidth + " HEIGHT=" + childnodes[currentNode].iconHeight + " ALIGN=TEXTTOP BORDER=0>&nbsp;");
        else
            TreeViewOutput.write("<IMG SRC='" + imageRoot + childnodes[currentNode].iconClosed + "' WIDTH =" + childnodes[currentNode].iconWidth + " HEIGHT=" + childnodes[currentNode].iconHeight + " ALIGN=TEXTTOP BORDER=0>&nbsp;");
        if (childnodes[currentNode].url == "")
            TreeViewOutput.write(childnodes[currentNode]Name + "<BR>\n");
        else
            TreeViewOutput.write("<A HREF='" + childnodes[currentNode].url + "' TARGET='" + childnodes[currentNode]Target + "' onMouseOver=\"window.status='" + childnodes[currentNode].url + "'; return true\" onMouseOut=\"window.status=''; return true\">" + childnodes[currentNode]Name + "</A><BR>\n");
        if (childnodes[currentNode].state == 1)
            drawBranchNodes(childnodes[currentNode].id, newImageString);
    }
}

The toggle function toggle the state of folders and triggers redraw of the treeview, the closeAllBranchNodes and openAllUnderNode are used to keep only one branch open at a time.

Function toggle(id)
{
    var nodeIndex = findNodeIndex(id);
    var currentState = TreeViewData[nodeIndex].state;
    if (oneBranch)
        closeAllBranchNodes();
    if (currentState == 1)
        TreeViewData[nodeIndex].state = 0;
    else
        TreeViewData[nodeIndex].state = 1;
    if (oneBranch)
        openAllUnderNode(TreeViewData[nodeIndex].parent);
    timeOut = setTimeout("drawTreeView()",100);
    return false;
}

function closeAllBranchNodes()
{
    var currentNode = 0;
    while (currentNode < TreeViewData.length)
    {
        currentNode++;
        if (TreeViewData[currentNode].state == 1)
            TreeViewData[currentNode].state = 0;
    }
    return;
}

function openAllUnderNode(id)
{
    var nodeIndex = findNodeIndex(id);
    if (id != 'root')
    {
        TreeViewData[nodeIndex].state = 1;
        openAllUnderNode(TreeViewData[nodeIndex].parent);
    }
    return;
}

The findNodeIndex function searches for the folder node that has the name given by the variable id and returns it's index in the main TreeViewData collection.

Function findNodeIndex(id)
{
    var currentNode = 0;
    while (currentNode < TreeViewData.length)
    {
        currentNode++;
        if (TreeViewData[currentNode].state != -1)
            if (TreeViewData[currentNode].id == id)
                return currentNode;
    }
    return -1;
}

The extractChildNodes function finds all the nodes that have the parent given in the variable parentNode and returns them in a collection.

Function extractChildNodes(parentNode)
{
    var childNodes = new Collection();
    var currentNode = 1;
    while (currentNode < TreeViewData.length)
    {
        currentNode++;
        if (TreeViewData[currentNode].parent == parentNode)
            childNodes.add(TreeViewData[currentNode]);
    }
    return childNodes;
}

The Collection and add functions implement the Collection type.

Function Collection()
{
    this.length = 0;
    this.add = add;
    return this;
}

function add(object)
{
    this.length++;
    this[this.length] = object;
}

The Node function implements the Node type and sets all the defaults if the input parameters have been left blank.

Function Node(id,parent,iconClosed,iconOpen,state,name,url,target,width,height)
{
    this.id = id;
    this.parent = parent;
    if (iconClosed != "")
        this.iconClosed = iconClosed;
    else
        if (state != -1)
            this.iconClosed = defaultFolderClosed;
        else
            if (parent != "")
                this.iconClosed = defaultLink;
            else
                this.iconClosed = defaultRoot;
    if (iconOpen != "")
        this.iconOpen = iconOpen;
    else
        this.iconOpen = defaultFolderOpen;
    this.state = state;
    this.name = name;
    this.url = url;
    if (target != "")
        this.target = target;
    else
        this.target = defaultTarget;
    this.iconWidth = width;
    this.iconHeight = height;
    return this;
}

© Copyright 2000 - 2008, Chris F Wood, All Rights Reserved.

Last modified on Monday, 1st January 1601.