A Dynamic Tree Menu System, Part 2
By Sam Reynoso
Read Part 1
In Part 1 we looked at the need for a tree-like navigational tool for Web sites.
We examined existing solutions and discussed how to create a custom solution using an XML file as a data source for
the tree structure. In this part, we'll take a high-level view at the HTML, ASP, and client-side JavaScript code
we'll need to accomplish this.
ASP for HTML Code Generation
One of the advantages of the tree navigation system is the ability to view the resulting HTML code
by selecting View/Source in the browser.
This is made possible by a core ASP subroutine that is called recursively
to traverse the XML tree. The HTML for displaying folders and documents is generated with each
call to the subroutine. When the last node of the XML tree has been processed, the ASP code will have
returned a complete HTML page containing the entire structure of the navigation menu.
When rendered by the browser, some elements will be collapsed by default until the user clicks to
expand them. Viewing the source would reveal all the nodes used in the tree menu and the
javascript necessary to control the expansion and collapse of menu items.
Javascript
The javascript consists of two functions that support toggling menu items open and closed.
The functions read from two arrays to determine which menu items to manipulate.
The first array contains integers that represent the IDs of all the expandable nodes on
the tree. The second array contains integers that represent the IDs of all expandable/collapsible
menu items. Since the code has to be able to generate any menu structure defined in the
XML file, we can't know the size of these arrays at design time. To get around this
problem, we embed ASP into the Javascript dynamically declare the arrays on the
server side at run-time. This is discussed in more detail below.
Introducing the Code
All the code necessary to generate the tree menu (except the XML) is stored in a
single file called menu.asp. At the top of the file we insert a basic style definition
to control the look of the text used in the menu items. We encapsulate our style into
a single class called node which we apply later on. The style
definition is presented below:
<STYLE TYPE="text/css">
<!--
.node { color: black;
font-family : "Helvetica", "Arial", "MS Sans Serif", sans-serif;
font-size : 9pt;}
.node A:link { color: black; text-decoration: none; }
.node A:visited { color: black; text-decoration: none; }
.node A:active { color: black; text-decoration: none; }
.node A:hover { color: black; text-decoration: none; }
-->
</STYLE>
|
Next, we load our XML file into the MSXML parser. We check for errors that can
occur along the way. The subroutine sbShowXMLParseError() displays a detailed
explanation of the parse error, if it is encountered, and helps pinpoint the
exact location of the error.
sXMLSourceFile = "menuitems.xml"
'Create instance of XML document object that we can manipulate
Set objDocument = Server.CreateObject("MSXML2.FreeThreadedDOMDocument.3.0")
if objDocument is nothing then
Response.Write "objDocument object not created<br>"
else
If Err Then
Response.Write "XML DomDocument Object Creation Error - <BR>"
Response.write Err.Description
else
'Load up the XML document into the XML object
objDocument.async = false
bLoaded = objDocument.load(Server.MapPath(sXMLSourceFile))
if (bLoaded = False) then
sbShowXMLParseError objDocument
else
...
|
Just before we begin drawing the tree menu, we give the developer an opportunity
to specify what folders should be expanded by default. This is done by putting
values into an array. When the menu is generated, the array is probed for elements
that match the current node. If a match is found, the folder is displayed as expanded.
'Set some folders to open by default.
dim arArray(3)
'Root value in our tree should be open by default
arArray(0) = objDocument.firstChild.getAttribute("value")
arArray(1) = "History"
|
Now we make our first call to the subroutine that will actually generate the
HTML for our navigation tree. The parameter iTotal is passed in by reference and
keeps track of the total number of elements in our tree. It will be used in the
Javascript portion of our page. The subroutine continues to call itself recursively
until all nodes of the objDocument object have been visited.
DisplayNode objDocument.childNodes, iTotal, sLeftIndent, arArray
|
Once DisplayNode has completed execution, we get to the javascript portion of our ASP page.
ASP code is embedded in the javascript array declaration. This permits us to be flexible
in the number of elements used in the tree menu. A For loop uses the value of iTotal
to iterate through a cycle equivalent to the number of nodes in the menu.
When the loop is complete, the page will contain a javascript array definition that
exactly matches the number of elements in the tree menu.
var arClickedElementID = new Array(<% for i = 1 to iTotal %> "<%=i%>"<%if i < iTotal then%>,<%end if%> <%next%>);
var arAffectedMenuItemID = new Array(<% for i = 1 to iTotal %> "<%=i+1%>"<%if i < iTotal then%>,<%end if%> <%next%>);
|
Once the ASP file has finished executing, the browser will receive a fully-formed
HTML page that displays a tree navigation menu. Viewing the sourcecode
will reveal only Javascript and HTML code. The content that was stored in the XML
file has been processed and is now a part of the HTML document.
Now that we've examined the purpose and general code for menu.asp from a very high-level perspective,
it's time that we drill into the guts of the page - the DisplayNode() function. In
Part 3 we'll tackle this task. (Do note, though, at any time you
may view a live demo of the dynamic tree navigation.)
Read Part 3!
|