You are here: Home > Articles > Article Display

Dynamic Tree Menu of your site

We'll see how to create a menu system that is cross-browser and includes all your site's folders/files. It uses ASP, XML and DHTML and by simply copying it to your site you have an instant Windows Explorer-like navigation of the contents.

Published: May 31, 2002
Tested with: ASP 3.0
Category: ASP
33,601 views

Introduction

In this article we will see how to create a dynamic folder tree control of your website's folders and files. It uses ASP, XML and DHTML and by simply copying it to your site you have an instant cross-browser, Windows Explorer-like navigation of the contents.

Features:

  • cross-browser compatible (NS4.x, NS6, IE4.x, IE5.x, IE6.x)
  • fast rendering
  • ability to refresh it at demand as changes are made to the file structure
  • easily customizable to include/exclude properties/nodes
  • control over what nodes to leave closed on loading for better performance

It is recommended, but not necessary, that you first read Generating an XML file of your website's folders/files, as well as Sam Reynoso's excellent article Enhancing the Dynamic Tree Menu System, which shows the logic used for displaying the Menu Tree. For this reason, I will only go through the changes needed to make this work. In the downloadable materials though, you can find the full source code.

To make this work for you

  • Create a folder in your website where you will copy all the code needed. You can name it whatever you like. I called mine navigation.
  • Download the zip file (top right-hand "Page Options" box) containing all the source code, and unzip it in your newly created folder.
  • Run the createxml.asp file to create the XML file of your site's contents.
  • When that is done, you can navigate to the default.html file which will render the Menu Tree.
Files needed and what they do
createxml.asp Traverses the file system of your website and creates an XML file of the contents.
aspRoutines.asp Loads the XML file and parses it out writing the HTML code for the Tree.
default.html Frameset of the left and right frames for viewing the Tree.
menu.asp Left frame of the frameset. Calls the aspRoutines.asp functions.
content.asp Right frame of the frameset. Accepts parameters sent from the links in the left frame and performs customized actions.
/images Folder where all the images reside for properly displaying the Tree.
functions.js Javascript functions to create the DHTML behaviors.
style.css Stylesheet for the Tree.
menuitems.xml XML file of the site's contents. Generated by createxml.asp.

Tree Menu explanation

Above is a snapshot of a typical example. The Tree Menu will show up in the left frame, while the right frame is reserved for sending the path of a folder/file to it and executing some action based on that. For example, if you are creating a content management system, you might actually open up text documents for editing and display contents or properties of a folder. In the materials available for download, I simply redirect to the document and display the folder path passed. Clicking on a folder icon will expand/contract to reveal the folder's contents.

Creating the XML file

The XML file is created by createxml.asp. The output of the XML file will be a little different this time from my previous article. This is an example of what it might look like eventually:

Example of what the XML file might look like

There are 3 main elements to this file:

  1. node name is either root, folder or document.
  2. name attribute is the OS name of the folder/file.
  3. path attribute is the virtual path of the folder/file.

There are some slight differences between this createxml.asp and the one in the previous article. Specifically:

1 ...
2 strXmlFile = "<?xml version=""1.0"" ?>" & strVbCrLf & "<root name=""Site Root"" path=""/"">" & strVbCrLf
3 ...
4 Set objFile = objFSO.CreateTextFile(Server.MapPath("menuitems.xml"), True, False)
5 ...
6 '-- append node to the output unless it is the root folder
7 If thisTree > 0 Then
8     strXmlFile = strXmlFile & "<folder name=""" & objFolder.Name & """ path=""" & strFolderURL & "/""" & " loadOnDemand=""" & fnLoadOnDemand(strFolderURL) & """>" & strVbCrLf
9     objFile.Write(strXmlFile)
10 End If
11 ...
12 '-- create output string for documents
13 strXmlFile = strXmlFile & "<document name=""" & objthisFile.Name & """ path=""" & strURL & """/>" & strVbCrLf
14 ...
15 Function fnLoadOnDemand(folderPath)
16     If folderPath = "/navigation" Then
17         fnLoadOnDemand = "yes"
18     Else
19         fnLoadOnDemand = "no"
20     End If
21 End Function
22 %>

Changes:

  • Addition of the xml version declaration in the XML file.
  • Change of location where the XML will be saved to.
  • There is no attribute type anymore (of root/folder/document). The node name is obvious enough and there is no need to repeat it.
  • name attribute replaces the old value attribute.
  • path attribute replaces the old url attribute.
  • A new function fnLoadOnDemand which goes along with the attribute loadOnDemand in each folder node.

The new function fnLoadOnDemand controls whether a particular node's children will be loaded in the resulting menu or not. It accepts the folder path as a parameter, and if it returns yes, it will load the folder's children, while if it returns no then it will not load them. This helps in deep sites where it takes a long time to render the menu tree. Customize this function to get the results you want. For example, I excluded the folder navigation manually by hard coding it in an IF statement. You might decide that your validation is to check how many files exist under a folder, and if there are let's say more than 30, then output a no.

Loading the XML file

This is done through a function called fnLoadXMLData in the aspRoutines.asp file. This file has only one change from the original: the way we load the data to be parsed. In our case, we are loading it from the XML file /navigation/menuitems.xml.

1 Function fnLoadXMLData(byRef objDocument)
2     Dim bResult
3     bResult = True
4     'Create instance of XML document object that we can manipulate
5     Set objDocument = Server.CreateObject("MSXML2.FreeThreadedDOMDocument.3.0")
6     If objDocument Is Nothing Then
7         Response.Write "objDocument object not created<br>"
8         bResult = False
9     Else
10         If Err Then
11             Response.Write "XML Document Object Creation Error - <br>"
12             Response.write Err.Description & "<br>"
13             bResult = False
14         Else
15             On Error resume Next
16             'Load up the XML document into the XML object
17             objDocument.async = False
18             bLoaded = objDocument.Load(Server.MapPath("menuitems.xml"))
19             If err <> 0 Then
20                 Response.Write err.Description & "<br>"
21                 bResult = False
22                 err = 0
23             End If
24         End If
25     End If
26     fnLoadXMLData = bResult
27 End Function

We create an instance of the MSXML component and load the menuitems.xml file we just created into it. If we encounter any errors, we output them appropriately.

Generating the HTML

Once the file is loaded, we call the sub DisplayNode, which is responsible for traversing the XML tree and properly displaying each menu item. This routine calls itself recursively and generates an HTML page containing javascript that handles showing/hiding the menu items. This sub is also in the aspRoutines.asp file.

1 Sub DisplayNode(ByVal objNodes, ByRef iElement, ByRef sLeftIndent, ByRef sOpenFolders)
2     ...
3     'Ignore NODE_TEXT node types
4     If oNode.nodeType = NODE_ELEMENT Then
5         sNodeType = oNode.nodeName
6         sAttrValue = oNode.getAttribute("name")
7         sURL = Server.URLEncode(oNode.getAttribute("path"))
8         ...
9         'Now display the document node
10 %>
11         <td height="16">
12             <img src=images/<%=fnChooseIcon(bIsLast, bIsRoot, sNodeType, bHasChildren, True)%> width=31 height=16 border=0>
13         </td>
14         <td>
15             <img src=images/pixel.gif width=2 height=1>
16         </td>
17         <td nowrap class=node>
18             <img src=images/pixel.gif width=2 height=1><a href="content.asp?document=<%=sURL%>" target=basefrm onClick=objPreviousLink=fnSelectItem(this,objPreviousLink)><%=sAttrValue%></a><img src=images/pixel.gif width=2 height=1>
19         </td>
20     </tr>
21 </table>
22 <%
23     'Otherwise this is a folder
24     Else
25         strLoadOnDemand = oNode.getAttribute("loadOnDemand")
26         'We set the LoadOnDemand value for the History folder
27         'because we want it to be populated only when clicked by the user
28         If (strLoadOnDemand = "yes") Then
29             sMode = "LoadOnDemand"
30         Else
31             sMode = ""
32         End If
33         ...
34         'Now display the folder
35 %>
36         <td height="16">
37             <img onclick="doChangeTree(this, arClickedElementID, arAffectedMenuItemID);" class=LEVEL<%=iElement%> src=images/<%=fnChooseIcon(bIsLast, bIsRoot, sNodeType, bHasChildren, bShowOpen)%> id=<%=iElement%> width=31 height=16 border=0 name="<%=sMode%>">
38         </td>
39         <td>
40             <img src=images/pixel.gif width=2 height=1>
41         </td>
42         <td nowrap class=node>
43             <img src=images/pixel.gif width=2 height=1><a href="content.asp?folder=<%=sURL%>" target=basefrm onClick=" objPreviousLink=fnSelectItem(this,objPreviousLink)"><%=sAttrValue%></a><img src=images/pixel.gif width=2 height=1>
44         </td>
45     </tr>
46 </table>
47 <%
48 ...

Changes:

  • sNodeType, sAttrValue, sURL are now capturing different attribute names - but they are still used the same way.
  • The path of the folder/file is now passed to the page content.asp file in the QueryString.
  • The sMode is now dependent on the folder node attribute loadOnDemand. If the value is yes, then the children will not be loaded unless that folder is clicked.

Conclusion

The best use that I can think of for something like this is a Content Management System or a Web-based File Manager. If you use it for this purpose, you can add some code that calls the createxml.asp file whenever you make a change to the filesystem, and then redirect to the default.html, so that the Menu reloads and the file changes are reflected in the Tree.

 



Other articles in this category
  1. Exporting Word files to HTML
    March 5, 2003
    In this article we will first discuss the case for and against using Word as your HTML editor. Then we will see how to properly save a Word file to smaller, more compact HTML files. Third and last, we will see how to do this through code, and possibly create a batch process for converting numerous Word files to HTML at once.
  2. GetRows VBScript Class - Part III: Paging the results
    January 16, 2003
    In Part I of this series, we saw how to create a VBScript class to query our database using the very fast GetRows() method, and return a recordset as a local array. In Part II, we extended the class to allow ADDing and UPDATEing a row in the database. In this Part III, we will expand the class further to allow pagination of the returned recordset.
  3. Generating an XML file of your website's folders/files
    May 24, 2002
    Using the File System Object (FSO) we can traverse through our website's contents and write them out in a nicely nested form in an XML file. We can then use that file for example, in a content management system or a TreeView control.
  4. Downloading any file using ASP, FSO and the ADODB Stream object
    May 8, 2002
    In this article, we will see how to allow a user to download any file from our web server. They will see a prompt, giving them the option of opening or saving it, rather than simply opening it which is the default. We can achieve this using the FSO and ADODB objects.
  5. Calling MS Access Parameterized Queries from ASP
    April 30, 2002
    Instead of passing a SQL query through your ASP code against Microsoft Access as you would normally do, you can use the Queries design interface to create them in Access and then call them from your ASP code. It makes things easier to edit and maintain, and the results are returned faster.