
Introduction
This article will demonstrate a simple and generic way to use XSLT to
generate a multi-level HTML tree menu from an XML source. JScript is used for
expanding and contracting the menu entries. (This requires a minimum of Internet
Explorer 5. I have not tested this on any other browsers.)
The XML Source
This is a subset of the source XML (full XML source is in the zip file).
<menu>
<entry>
<text>In-House</text>
<url>InHouse.htm</url>
<entry>
<text>Web Development</text>
<url>WebDev.htm</url>
</entry>
</entry>
</menu>
The XSLT
For each menu entry, the XSLT processes it, then drills down until it has
processed that entry's last child.
="1.0"
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="//menu/entry">
<xsl:call-template name="SubMenu">
<xsl:with-param name="strCSS">Parent IsVisible</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="SubMenu">
<xsl:param name="strCSS" />
<xsl:variable name="strURL" select="url" />
<div class="{$strCSS}">
<xsl:choose>
<xsl:when test="count(entry) > 0">
-->
<input type="hidden" id="hidIsExpanded" value="0" />
<label id="lblExpand" class="Expander" onclick="ExpanderClicked()">+
</label>
</xsl:when>
<xsl:otherwise>
<label class="Expander"> </label>
</xsl:otherwise>
</xsl:choose>
<a href="{$strURL}"><xsl:value-of select="text" /></a>
<xsl:for-each select="entry">
<xsl:call-template name="SubMenu">
<xsl:with-param name="strCSS">NotVisible</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
Transforming the XML on the server
ASP is used to perform server-side transformation of the XML.
<%
dim xmlMenu
dim xslMenu
set xmlMenu = server.CreateObject("Microsoft.XMLDOM")
xmlMenu.async = false
xmlMenu.load server.MapPath("TreeFromXMLUsingXSLT.xml")
set xslMenu = server.CreateObject("Microsoft.XMLDOM")
xslMenu.async = false
xslMenu.load server.MapPath("TreeFromXMLUsingXSLT.xsl")
Response.Write xmlMenu.transformNode(xslMenu)
set xmlMenu = nothing
set xslMenu = nothing
%>
Controlling the menu entries
Client-side JScript is used to first determine whether a menu entry has been
expanded or collapsed, and then adjusts the selected menu entry's style
settings.
<script language="jscript">
function ExpanderClicked()
{
var ctlExpander = event.srcElement;
var ctlSelectedEntry = ctlExpander.parentElement;
var colChild = ctlSelectedEntry.children.tags("DIV");
if(colChild.length > 0)
{
var strCSS;
var ctlHidden = ctlSelectedEntry.all("hidIsExpanded");
if(ctlHidden.value == "1")
{
ctlExpander.innerHTML = "+ ";
ctlHidden.value = "0";
strCSS = "NotVisible";
}
else
{
ctlExpander.innerHTML = "- ";
ctlHidden.value = "1";
strCSS = "IsVisible";
}
for(var intCounter = 0; intCounter < colChild.length; intCounter++)
{
colChild[intCounter].className = strCSS;
}
}
}
</script>
Style setting
CSS settings are used to specify if a menu entry should be visible or hidden.
The IsVisible class sets the element to be displayed,
as a block element. The NotVisible class sets the
element to be hidden.
body
{
font-family: Verdana;
font-size: x-small;
}
.IsVisible
{
display: block;
}
.NotVisible
{
display: none;
}
.Expander
{
cursor: hand;
font-family: Courier;
}
.Parent DIV
{
margin-Left: 15px !important;
}
The last setting (.Parent DIV) indicates that only the
elements below the one with the "Parent" class, will have a margin. This is to
prevent the root menu entries from also having a margin.
Using images instead of + and -
It is fairly simple to show images instead of a "+" and "-" next to each
entry. To do this, alter the XSLT and replace the <label> element that is next to the link, with an <img> element. In the JScript, instead of setting the
InnerHTML property of the ctlExpander variable, set the src
attribute to show a different image. Here is an example:
