Navigation in several levels (nested ul's) with a visible backwards trace?
Hi!
I'd like to know if anyone has a nice example of an XSLT that let's me generate a nested navigation in several levels (3 or more) that also shows a trace back to the top level? The HTML generated should look something like this, if I'm currently on the page called SubSub3:
When the user first enters the site only the top level is visible. A click on one of the top level links leads to that top level page and the navigation reveals any child pages that top page has. A click on any of the child page links displays that (child) page and reveals any of its child pages.
In the example above, the user has clicked (in order): Top3 -> Sub3 -> SubSub3. The class "active" is used for displaying the backward trace - it reveals which links the user has clicked to get to the current page. So only one tree is visible at once and it's only as deep as the number of clicks the user has made.
I've done similar things in the past, but it's messy and doesn't have this exact behavior.
This (if i remember correctly) should do what you want (it's from the business website starter package). You'll have to change currentli and current to be what you'd like
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:Stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt" xmlns:umbraco.library="urn:umbraco.library" exclude-result-prefixes="msxml
umbraco.library">
<xsl:output method="html" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<!-- update this variable on how deep your nav should be -->
<xsl:variable name="maxLevel" select="6"/>
<xsl:template match="/">
<xsl:variable name="parentNode" select="$currentPage/ancestor-or-self::node [@level=2]" />
<xsl:if test="string($parentNode/@id) != ''">
<h2><a href="{umbraco.library:NiceUrl($parentNode/@id)}"><xsl:value-of select="$parentNode/@nodeName"/></a></h2>
<xsl:call-template name="drawNodes">
<xsl:with-param name="parent" select="$parentNode"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="drawNodes">
<xsl:param name="parent"/>
<xsl:if test="umbraco.library:IsProtected($parent/@id, $parent/@path) = 0 or (umbraco.library:IsProtected($parent/@id, $parent/@path) = 1 and umbraco.library:IsLoggedOn() = 1)">
<ul class="subnav">
<xsl:for-each select="$parent/node [string(./data [@alias='umbracoNaviHide']) != '1' and @level <= $maxLevel and @nodeTypeAlias!='NewsItem' and @nodeTypeAlias!='Event' and @nodeTypeAlias!='DateFolder']">
<li>
<xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
<xsl:attribute name="class">currentli</xsl:attribute>
</xsl:if>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
<xsl:attribute name="class">current</xsl:attribute>
</xsl:if>
<xsl:value-of select="@nodeName"/></a>
<xsl:if test="count(./node [string(./data [@alias='umbracoNaviHide']) != '1' and @level <= $maxLevelForSitemap]) > 0">
<xsl:call-template name="drawNodes">
<xsl:with-param name="parent" select="."/>
</xsl:call-template>
</xsl:if>
</li>
</xsl:for-each>
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
If your current solution is "messy", I'd suggest you start with this (very basic) one to get a feel for how to meet your specific requirement (the 'active' class), and work your way through adding the additional features from Dan's code above (e.g. the IsProtected() stuff).
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:param name="currentPage" />
<xsl:variable name="root" select="$currentPage/ancestor-or-self::root" />
<!-- Choose the top level for the navigation -->
<xsl:variable name="startLevel" select="'1'" />
<xsl:template match="/">
<ul>
<!-- Apply templates to all nodes at the start level -->
<xsl:apply-templates select="$root//node[@level = $startLevel]" />
</ul>
</xsl:template>
<xsl:template match="node">
<li>
<!-- If 'currentPage' is in this branch, add the class 'active' -->
<xsl:if test="descendant-or-self::node[@id = $currentPage/@id]">
<xsl:attribute name="class">active</xsl:attribute>
</xsl:if>
<!-- Output the name -->
<xsl:apply-templates select="@nodeName" />
<!-- Again, if 'currentPage' is in this branch AND there's at least one node that's not hidden, add a new level -->
<xsl:if test="descendant-or-self::node[@id = $currentPage/@id] and node[not(data[@alias = 'umbracoNaviHide'] = 1)]">
<ul>
<xsl:apply-templates select="node" />
</ul>
</xsl:if>
</li>
</xsl:template>
<!-- Don't output these -->
<xsl:template match="node[data[@alias = 'umbracoNaviHide'] = 1]" />
</xsl:stylesheet>
I found Chriztians example very useful and easy to modify to suit my specific needs! It generates exactly the code I want. Since this is a public website I don't need the code that checks if pages are protected or if the user is logged in. I will need to add some checks for the umbracoNaviHide parameter though since there will be pages that I don't want to render in the navigation.
Navigation in several levels (nested ul's) with a visible backwards trace?
Hi!
I'd like to know if anyone has a nice example of an XSLT that let's me generate a nested navigation in several levels (3 or more) that also shows a trace back to the top level? The HTML generated should look something like this, if I'm currently on the page called SubSub3:
When the user first enters the site only the top level is visible. A click on one of the top level links leads to that top level page and the navigation reveals any child pages that top page has. A click on any of the child page links displays that (child) page and reveals any of its child pages.
In the example above, the user has clicked (in order): Top3 -> Sub3 -> SubSub3. The class "active" is used for displaying the backward trace - it reveals which links the user has clicked to get to the current page. So only one tree is visible at once and it's only as deep as the number of clicks the user has made.
I've done similar things in the past, but it's messy and doesn't have this exact behavior.
Thankyou!
/Thomas Kahn
Hi Thomas
This (if i remember correctly) should do what you want (it's from the business website starter package). You'll have to change currentli and current to be what you'd like
Dan
Hi Thomas,
If your current solution is "messy", I'd suggest you start with this (very basic) one to get a feel for how to meet your specific requirement (the 'active' class), and work your way through adding the additional features from Dan's code above (e.g. the IsProtected() stuff).
Cheers,
Chriztian
Thanks guys!
I found Chriztians example very useful and easy to modify to suit my specific needs! It generates exactly the code I want. Since this is a public website I don't need the code that checks if pages are protected or if the user is logged in. I will need to add some checks for the umbracoNaviHide parameter though since there will be pages that I don't want to render in the navigation.
/Thomas Kahn
is working on a reply...