Creating a Multilevel Menu using XSLT

Creating a Multilevel Menu using XSLT

2 60155
Creating a multilevel menu using xslt

Multilevel menu with XSLT. Today we continue the practice of using XSLT. And lets consider a very entertaining task – the creation of navigation menu through XSL transformations. Someone will think that this is a fairly simple task, but we complicate it in advance – the menu should be multilevel. That is mean an unlimited number of sublevels. As initial data we will have an XML file with defined structure. At the output we will expect our multi-level drop-down menu.

Here are samples and downloadable package:

Live Menu Demo


download in package


Ok, download the source files and lets start coding !

Step 1. Source XML

Firstly lets define our structure of initial menu:


<?xml version="1.0" encoding="UTF-8"?>
    <top title="Element 1" url="#">
        <link title="subelement 1#1" url="#"></link>
        <link title="subelement 1#2" url="#">
            <link title="subelement 1#2#1" url="#"></link>
            <link title="subelement 1#2#2" url="#"></link>
            <link title="subelement 1#2#3" url="#"></link>
        <link title="subelement 1#3" url="#"></link>
    <top title="Element 2" url="#">
        <link title="subelement 2#1" url="#"></link>
        <link title="subelement 2#2" url="#">
            <link title="subelement 2#2#1" url="#"></link>
            <link title="subelement 2#2#2" url="#"></link>
            <link title="subelement 2#2#3" url="#"></link>
        <link title="subelement 2#3" url="#"></link>
    <top title="Element 3" url="#">
        <link title="subelement 3#1" url="#"></link>
        <link title="subelement 3#2" url="#">
            <link title="sub 3#2#1" url="#"></link>
            <link title="sub 3#2#2" url="#"></link>
            <link title="sub 3#2#3" url="#">
                <link title="sub 3#2#3#1" url="#">
                    <link title="sub 1" url="#"></link>
                    <link title="sub 2" url="#"></link>
                    <link title="sub 3" url="#"></link>
                <link title="sub 3#2#3#2" url="#"></link>
                <link title="sub 3#2#3#3" url="#"></link>
        <link title="subelement 3#3" url="#"></link>
    <top title="Element 4" url="#">
        <link title="subelement 4#1" url="#"></link>
        <link title="subelement 4#2" url="#"></link>
        <link title="subelement 4#3" url="#"></link>

Step 2. PHP

Here are easy PHP file which we using for xsl transformation:


// create DomDocument and load our menu
$xml = new DOMDocument;
$xsl = new DOMDocument;
$xsl->load('xslt/template.xslt'); // importing xslt
$proc = new XSLTProcessor; // creating xslt processor
$proc->importStyleSheet($xsl); // attaching xsl rules
echo $proc->transformToXML($xml); // output

Step 3. CSS

Here are used CSS file. It contain CSS of our future menu too:


body{background:#eee;font-family:Verdana, Helvetica, Arial, sans-serif;margin:0;padding:0}
.main{background:#FFF;width:698px;height:440px;font-size:80%;border:1px #000 solid;margin:3.5em auto 2em;padding:1em 2em 2em}
#background {background:url(paper.jpg);}
/*menu styles*/
.preload1 {background: url(tab.png);}
.preload2 {background: url(tabb_on.png);}
.menu2 {padding:0 0 0 32px; margin:0; list-style:none; height:36px; position:relative; z-index:500; font-family:arial, verdana, sans-serif;}
.menu2 {display:block; float:left;}
.menu2 li a.top_link {display:block; float:left; height:36px; color:#444; text-decoration:none; }
.menu2 li a.top_link span {display:block; height:36px; float:left; line-height:27px; font-size:11px; font-weight:bold; padding:0 10px; cursor:pointer; background: url(tab_off.png) no-repeat;}
.menu2 li a.top_link b {display:block; width:15px; height:36px; float:left; background: url(tabb_off.png) no-repeat;}
.menu2 li a.top_link b.down {background: url(tabdown_off.png) no-repeat;}
.menu2 li a.top_link:hover span {color:#fff; background: url(tab.png) no-repeat; line-height:26px;}
.menu2 li a.top_link:hover b {color:#fff; background: url(tabb_on.png) no-repeat;}
.menu2 li:hover > a.top_link span {color:#fff; background: url(tab.png) no-repeat; line-height:26px;}
.menu2 li:hover > a.top_link b {color:#fff; background: url(tabb_on.png) no-repeat;}
.menu2 li:hover > a.top_link b.down {color:#fff; background: url(tabdown_on.png) no-repeat;}
.menu2 table {border-collapse:collapse; width:0; height:0; position:absolute; top:0; left:0;}
/* Default link styling */
/* Style the list OR link hover. Depends on which browser is used */
.menu2 a:hover {visibility:visible; position:relative; z-index:200;}
.menu2 li:hover {position:relative; z-index:200;}
/* keep the 'next' level invisible by placing it off screen. */
.menu2 ul,
.menu2 :hover ul ul,
.menu2 :hover ul :hover ul ul,
.menu2 :hover ul :hover ul :hover ul ul,
.menu2 :hover ul :hover ul :hover ul :hover ul ul {position:absolute; left:-9999px; top:-9999px; width:0; height:0; margin:0; padding:0; list-style:none;}
.menu2 :hover ul.sub {left:0; top:32px; white-space:nowrap; width:120px; height:auto; z-index:300;}
.menu2 :hover ul.sub li {display:block; height:20px; float:left; width:120px; font-weight:normal; background: url(submid.png);}
.menu2 :hover ul.sub {background: url(submid_r.png);}
.menu2 :hover ul.sub li a {display:block; position:relative; font-size:11px; height:20px; width:120px; line-height:20px; text-indent:10px; color:#000; text-decoration:none;background:url(trans.gif);}
.menu2 :hover ul.sub li a:hover {color:#fff;}
.menu2 :hover ul.sub :hover > a {color:#fff;}
.menu2 :hover ul :hover ul,
.menu2 :hover ul :hover ul :hover ul,
.menu2 :hover ul :hover ul :hover ul :hover ul,
.menu2 :hover ul :hover ul :hover ul :hover ul :hover ul
{left:120px; top:0; white-space:nowrap; width:120px; z-index:400; height:auto;}
.menu2 :hover ul.sub li.subtop,
.menu2 :hover ul :hover ul li.subtop,
.menu2 :hover ul :hover ul :hover ul li.subtop,
.menu2 :hover ul :hover ul :hover ul :hover ul li.subtop
.menu2 :hover ul.sub li.flytop,
.menu2 :hover ul :hover ul li.flytop,
.menu2 :hover ul :hover ul :hover ul li.flytop,
.menu2 :hover ul :hover ul :hover ul :hover ul li.flytop
{background: url(subtop_r.png);}
.menu2 :hover ul.sub li.subbot,
.menu2 :hover ul :hover ul li.subbot,
.menu2 :hover ul :hover ul :hover ul li.subbot,
.menu2 :hover ul :hover ul :hover ul :hover ul li.subbot
{height:30px; background:url(subbottom.png);}
.menu2 :hover ul.sub li.flybot,
.menu2 :hover ul :hover ul li.flybot,
.menu2 :hover ul :hover ul :hover ul li.flybot,
.menu2 :hover ul :hover ul :hover ul :hover ul li.flybot
{height:30px; background: url(subbottom_r.png);}

As you can see – menu itself using some images, and I not sure that need put it in our article – they always will available in our downloadable package.

Step 4. XSLT

And, the delicacy – used XSLT rules:


<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="">
    <xsl:output method="html" doctype-system="" doctype-public="-//W3C//DTD HTML 4.01//EN" indent="yes" />
    <xsl:template match="/">
        <html xml:lang="en">
                <link media="all" href="css/styles.css" type="text/css" rel="stylesheet"/>
                <div class="main" id="background">
                    <!-- The menu starts here -->
                    <ul class="menu2">
                        <xsl:for-each select="menu/top">
                            <li class="top">
                                <a class="top_link">
                                    <xsl:attribute name="href">
                                        <xsl:value-of select="@url"/>
                                        <xsl:value-of select="@title"/>
                                      <xsl:if test="count(link) > 0">
                                        <xsl:attribute name="class">down</xsl:attribute>
                                <xsl:call-template name="nested_levels">
                                    <xsl:with-param name="level" select="1"/>
                <xsl:comment>Copyright: AndrewP</xsl:comment>
                <hr style="clear:both;" />
                    <a href="">back to original article page</a>
    <xsl:template name="nested_levels">
        <xsl:param name="level" select="0"/>
        <xsl:if test="count(link) > 0">
                <xsl:if test="$level = 1">
                    <xsl:attribute name="class">sub</xsl:attribute>
                <xsl:for-each select="link">
                        <xsl:when test="count(link) = 0 and position()=1">
                            <xsl:attribute name="class">subtop</xsl:attribute>
                        <xsl:when test="count(link) &gt; 0 and position()=1">
                            <xsl:attribute name="class">flytop</xsl:attribute>
                        <xsl:when test="count(link) &gt; 0 and not(position()=last())">
                            <xsl:attribute name="class">fly</xsl:attribute>
                        <xsl:when test="count(link) = 0 and position()=last()">
                            <xsl:attribute name="class">subbot</xsl:attribute>
                        <xsl:when test="count(link) &gt; 0 and position()=last()">
                            <xsl:attribute name="class">flybot</xsl:attribute>
                            <xsl:attribute name="href">
                                <xsl:value-of select="@url"/>
                            <xsl:value-of select="@title"/>
                            <xsl:value-of select="$level"/>
                        <xsl:call-template name="nested_levels">
                            <xsl:with-param name="level" select="$level + 1"/>

This script will generate full HTML of page, with including css styles, defining doctype etc. I hope that most of code is pretty understandable, note one point – to provide multilevel support I made special template ‘nested_levels’ which I using to generate code for inner sublevels in recursion, so you can have even 5 or 10 sublevels if you want.

Live Demo


I hope that today’s article was very interesting, and possible you even will change your oppinion about using xsl in your projects, anyway – this is good practic for our brains to play with xsl. Good luck!



    • You mean that instead
      <xsl:template name="nested_levels">
      better to create something similar
      <xsl:template match="link" mode="mode_link">

      hmm, yes, possible too, thanks for suggestion

Leave a Reply