Click here to Skip to main content
Click here to Skip to main content

Using XSLT to generate a multi-level tree menu from XML

By , 28 Feb 2002
 

Sample Image - TreeFromXMLUsingXSLT.gif

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.

<?xml version="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">
    <!-- Element has children, it can be expanded -->
    <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
   
   'Get the source XML
   set xmlMenu = server.CreateObject("Microsoft.XMLDOM")
   xmlMenu.async = false
   xmlMenu.load server.MapPath("TreeFromXMLUsingXSLT.xml")
   
   'Get the XSLT to transform the XML
   set xslMenu = server.CreateObject("Microsoft.XMLDOM")
   xslMenu.async = false
   xslMenu.load server.MapPath("TreeFromXMLUsingXSLT.xsl")
   
   'Transform the source XML using XSLT
   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()
{
    //Get the element that was clicked
    var ctlExpander = event.srcElement;
    var ctlSelectedEntry = ctlExpander.parentElement;
    //Get all the DIV elements that are direct descendants
    var colChild = ctlSelectedEntry.children.tags("DIV");
    if(colChild.length > 0)
    {
        var strCSS;
        //Get the hidden element that 
        //indicates whether or not entry is expanded
        var ctlHidden = ctlSelectedEntry.all("hidIsExpanded");
      
        if(ctlHidden.value == "1")
        {
            //Entry was expanded and is being contracted
            ctlExpander.innerHTML = "+ ";
            ctlHidden.value = "0";
            strCSS = "NotVisible";
        }
        else
        {
            //Entry is being expanded
            ctlExpander.innerHTML = "- ";
            ctlHidden.value = "1";
            strCSS = "IsVisible";
        }
        //Show all the DIV elements that are direct children
        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:

Sample Image - TreeFromXMLUsingXSLT2.gif

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

MS le Roux
Web Developer
South Africa South Africa
Member
I live in the Northern Suburbs of Cape Town (South Africa).

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: mozillamembermrameshchandra6 May '05 - 23:36 
Hi,
 
have you got any solution for this? if yes, please forward me to mrameshchandra@gmail.com. This is very urgent.
 
Thanks and regards,
mrc
GeneralRe: mozillamemberscotiniotis28 Jun '07 - 7:58 
In Firefox 2.0.0.4 the menu behavior is non existent.
The parent/children are all shown one under another
Any clues? Perhaps a javascript method is defined only in IE?
GeneralTransfom using javascriptmemberfrostie23 Feb '03 - 22:58 
Thanks for the menu, just what I was looking for....
 
I wanted to use without asp, so I moved the javascript code to the html page containing the javascript transform.
 
It all works except the very top item, and IE 6 gives the following error:
 
Line: 21
Char: 3
Error: Object Required
Code: 0
 
Any thoughts please,
below is my html code.
 
Thanks
 
Neil
 

<body>
<script language="javascript">
function ExpanderClicked()
{
//Get the element that was clicked
var ctlExpander = event.srcElement;
var ctlSelectedEntry = ctlExpander.parentElement;
//Get all the DIV elements that are direct descendants
var colChild = ctlSelectedEntry.children.tags("DIV");
if(colChild.length > "0")
{
var strCSS;
//Get the hidden element that indicates whether or not entry is expanded
var ctlHidden = ctlSelectedEntry.all("hidIsExpanded");

if(ctlHidden.value == "1")
{
//Entry was expanded and is being contracted
ctlExpander.src = "images/album.gif";
ctlHidden.value = "0";
strCSS = "NotVisible";
}
else
{
//Entry is being expanded
ctlExpander.src = "images/album.gif";
ctlHidden.value = "1";
strCSS = "IsVisible";
}
//Show all the DIV elements that are direct children
for(var intCounter = 0; intCounter < colChild.length; intCounter++)
{
colChild[intCounter].className = strCSS;
}
}
}
</script>
 

<script type="text/javascript">
// Load XML
var xml = new ActiveXObject("Msxml2.DOMDocument")
xml.async = false
xml.load("contents.xml")
 
// Load XSL
var xsl = new ActiveXObject("Msxml2.DOMDocument")
xsl.async = false
xsl.load("contents1.xsl")
 
// Transform
document.write(xml.transformNode(xsl))
 
</script>


GeneralRe: Transfom using javascriptmemberMS le Roux24 Feb '03 - 1:01 
I copied your code into a .htm file and I don't get an error. From the error message I suspect that the problem is with a null variable. I suggest you "debug" the code by putting in an alert every so often to determine if any of your variables are null.

GeneralRe: Transfom using javascriptmemberfrostie24 Feb '03 - 5:02 
I'm unable to figure it out, could you have a look at the rest of my code please?
 
here is the rest of the code:
contents1.xsl:
 


 



Parent IsVisible



 








<!-- Element has children, it can be expanded -->
<input type="hidden" id="hidIsExpanded" value="0" />
<img src="images/album.gif" id="lblExpand" önclick="ExpanderClicked()" />


<img src="images/cd.gif" class="Expander" />






NotVisible



 


 
contents.xml
 


Bryan Adams
The Best Of Me

 

Christina Aguilera
Christina Aguilera

 

B*Witched
B*Witched


 

GeneralRe: Transfom using javascriptmemberfrostie24 Feb '03 - 5:04 
Sleepy | :zzz: ****ignore other msg, forgot to ignore html****
 
I'm unable to figure it out, could you have a look at the rest of my code please?
 
here is the rest of the code:
contents1.xsl:
 
<code><?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
<xsl:template match="/">
<xsl:for-each select="//db1/cdmenu">
   <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="Artist" />

<div class="{$strCSS}">
   <xsl:choose>
   <xsl:when test="count(cdmenu) &gt; 0">
      <!-- Element has children, it can be expanded -->
      <input type="hidden" id="hidIsExpanded" value="0" />
      <img src="images/album.gif" id="lblExpand" onclick="ExpanderClicked()"   />  
     </xsl:when>
   <xsl:otherwise>
      <img src="images/cd.gif" class="Expander" />
   </xsl:otherwise>
   </xsl:choose>
  
   <a href="data.html#{$strURL}" target="DataF" class="pnpNavigatorLink"><xsl:value-of select="Artist" /></a>
   <xsl:for-each select="cdmenu">
   <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>
</code>
 
contents.xml
 
<code><db1>
     <cdmenu>
          <Artist>Bryan Adams</Artist>
          <cdmenu><Artist>The Best Of Me</Artist></cdmenu>
     </cdmenu>
 
     <cdmenu>
          <Artist>Christina Aguilera</Artist>
          <cdmenu><Artist>Christina Aguilera</Artist></cdmenu>
     </cdmenu>
 
     <cdmenu>
          <Artist>B*Witched</Artist>
          <cdmenu><Artist>B*Witched</Artist></cdmenu>
     </cdmenu>
</db1></code>
 

GeneralRe: Transfom using javascriptmemberfrostie24 Feb '03 - 5:30 
solved it, It needs text before the function as a title in plain html.
 

Thanks
 
NeilBig Grin | :-D
GeneralPersistencememberDACS4 Jul '02 - 23:01 
Is it possible to ad Persistence to the menu, so the tree remains in the same state after opening a new page.
 
Perhaps with a session variable

Regards
 
Claus

GeneralCan't get it working!memberchris B13 Jun '02 - 23:27 
i'm sorry but i'm new to XML and i can't get the script working
 
i've downloaded the .zip file and extract all the files into my web server and run the script.
 
all i get is :
 

Development Parent IsVisible
+      NotVisible
 

when i click the + button nothing happens!
 
Any ideas?  
 
ps: i'm using Internet explorer 6
 
chris B
GeneralRe: Can't get it working!memberMS le Roux17 Jun '02 - 20:25 
AFAIK all you need to run this is IE5.5 or greater, and the MSXML parser Version 3. Since the tree isn't rendering properly ("IsVisible" and "NotVisible" are CSS classes, and you should definitely not be seeing those), I'm guessing it might be that you don't have the correct parser?
 
Do any script errors appear when the tree loads?
GeneralRe: Can't get it working!memberStephenWilliams9 Dec '02 - 0:09 
I got the same problem with the tree not rendering properly. Replacing the XML parser from "Microsoft.XMLDOM" to "Msxml2.DOMDocument" seemed to do the trick.
 
Note - Not sure if this is the best solution - I'm new to this sort of thing.
 

QuestionPictures?memberDomenic [CPUA 0x1337]1 Mar '02 - 3:06 
I am sorry, I don't have the patience to peruse this potentially useful article until can see the end result.
 
-Domenic Denicola- [CPUA 0x1337]
Geekn
MadHamster Creations

AnswerRe: Pictures?memberMarSCoZa1 Mar '02 - 3:15 
Confused | :confused: The image is at the top of the page (The menu in the image contains "Development" at the top, with "In-House" and "Microsoft" on the next level, etc.) - that is basically what the menu looks like.
Of course, it is entirely possible to add snazzy little images to indicate whether a menu entry is expanded or collapsed, but I figured "+" and "-" are okay.

GeneralRe: Pictures?memberDomenic [CPUA 0x1337]1 Mar '02 - 3:19 
I'm dumb Smile | :)
 
In that case, good work. Nice article! And sorry about my stupidity
 
-Domenic Denicola- [CPUA 0x1337]
Geekn
MadHamster Creations

GeneralRe: Pictures?memberAnand Bisht25 Nov '02 - 0:35 
yes u are Big Grin | :-D
 
hey just kidding.. guys do post some good XML examples and tutorial, Also how to working with JSP as well as ASP too.. and even where can we decide to have xml in business logic. For eample I want to develop informatics banking project or shopping carts?
 
I know what it does and how its work now I am doing some business logic brain stroming. Please do joing me and lets share knowledge.
 
Thanks and regards.
Anand Bisht Confused | :confused:
GeneralRe: Pictures?memberMS le Roux25 Nov '02 - 0:51 
Other than having extremely limited space, I can't think of a reason why not to use XML. Both the concept and the format are simple - after all, it's basically a formatted string. And it works well on the user interface layer. You don't strictly need XSLT to transform XML for display; theres DOM, SAX, etc. (Server-side you can use pretty much what you want.)
BTW, if you're using .NET, you'll find that XML is a major part of it.

AnswerRe: Pictures?memberMarSCoZa1 Mar '02 - 4:45 
I've modified the article slightly to show how to replace the "+" and "-" with images. I've added an image to the bottom of the article to show an example of this.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 1 Mar 2002
Article Copyright 2002 by MS le Roux
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid