Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Thomas Kahn 602 posts 506 karma points
    Nov 30, 2009 @ 21:56
    Thomas Kahn
    0

    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:

    <ul>
    <li>Top1</li>
    <li>Top2</li>
    <li class="active">Top3
    <ul>
    <li>Sub1</li>
    <li>Sub2</li>
    <li class="active">Sub3
    <ul>
    <li>SubSub1</li>
    <li>SubSub2</li>
    <li class="active">SubSub3</li>
    </ul>
    </li>
    </ul>
    </li>
    <li>Top4</li>
    <li>Top5</li>
    </ul>

    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

  • dandrayne 1138 posts 2262 karma points
    Nov 30, 2009 @ 22:04
    dandrayne
    1

    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

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xsl:Stylesheet [ <!ENTITY nbsp "&#x00A0;"> ]>
    <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 &lt;= $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 &lt;= $maxLevelForSitemap]) &gt; 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>

    Dan

     

  • Chriztian Steinmeier 2800 posts 8791 karma points MVP 8x admin c-trib
    Nov 30, 2009 @ 23:25
    Chriztian Steinmeier
    1

    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).

    <?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>

    Cheers,

    Chriztian

  • Thomas Kahn 602 posts 506 karma points
    Dec 01, 2009 @ 09:42
    Thomas Kahn
    0

    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

Please Sign in or register to post replies

Write your reply to:

Draft