Copied to clipboard

Flag this post as spam?

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


  • Jan Itor 3 posts 23 karma points
    Oct 28, 2009 @ 13:07
    Jan Itor
    0

    Repeating Child nodes in Nav menu

    I have been staring at this problem for two days now and cannot seem to resolve it.  When I access my top-level pages, everything works fine.  However, when I attempt to click on a page that is a child of another page, it duplicates itself in my navigation menu.  My XSLT is not that sharp, so I may be overlooking something simple here, but I would appreciate any help that is given.

    <?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="xml" omit-xml-declaration="yes"/>

    <xsl:param name="currentPage" />

    <xsl:template match="/">
    <div>
    <xsl:variable name="rootNode" select="$currentPage/ancestor-or-self::node [@level=2]" />
    <xsl:for-each select="$currentPage/ancestor-or-self::node/node [@level=2 and string(data[@alias='umbracoNaviHide']) != '1']">
    <!-- Add the class selected if the currentpage or parent nodes (up the tree to the root) ID matches our current node ID in the for each loop -->
    <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
    <p class="mainNavselected">
    <img src="images/arrow_red.gif" />&nbsp;
    <a href="{umbraco.library:NiceUrl(@id)}" atl="" class="main"><xsl:value-of select="data[@alias='umbracoMenuLinkTitle']" /></a>
    </p>
    </xsl:if>
    <xsl:if test="$currentPage/ancestor-or-self::node/@id != current()/@id">
    <p class="mainNav">
    <img src="images/arrow.gif" />&nbsp;
    <a href="{umbraco.library:NiceUrl(@id)}" atl="" class="main"><xsl:value-of select="data[@alias='umbracoMenuLinkTitle']" /></a>
    </p>
    </xsl:if>
    </xsl:for-each>
    <xsl:variable name="rootNode2" select="$currentPage/ancestor-or-self::node [@level=3]" />
    <xsl:for-each select="$currentPage/ancestor-or-self::node/node [@level=3 and string(data[@alias='umbracoNaviHide']) != '1']">
    <!-- Add the class selected if the currentpage or parent nodes (up the tree to the root) ID matches our current node ID in the for each loop -->
    <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
    <p class="secnav">&nbsp;&nbsp;
    <a href="{umbraco.library:NiceUrl(@id)}" atl="" class="secnavlink" style="color:#bf1b11"><xsl:value-of select="data[@alias='umbracoMenuLinkTitle']" /></a>
    </p>
    </xsl:if>
    <xsl:if test="$currentPage/ancestor-or-self::node/@id != current()/@id">
    <p class="secnav">&nbsp;&nbsp;
    <a href="{umbraco.library:NiceUrl(@id)}" atl="" class="main"><xsl:value-of select="data[@alias='umbracoMenuLinkTitle']" /></a>
    </p>
    </xsl:if>
    </xsl:for-each>
    </div>
    </xsl:template>
    </xsl:stylesheet>
  • Chris Houston 535 posts 980 karma points MVP admin c-trib
    Oct 28, 2009 @ 14:04
    Chris Houston
    0

    Hi Jan,

    Not quite sure why you are written your navigation in the way you have done?

    I.e. Why the hard coded CSS and class names for specific levels as this limits your XSLT to only be used for a 2 level navigation. I have just cleaned up my standard navigation XSLT that I use to build navigations in the majority of sites that I have developed. You will need to add your CSS to style the navigation based on the top level UL ( see below )

    <?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="xml" omit-xml-declaration="yes" />
    <xsl:param name="currentPage"/>

    <xsl:template match="/">
    <ul id="nav">
    <li>
    <xsl:if test="$currentPage/@nodeName = 'Home'">
    <xsl:attribute name="class">current</xsl:attribute>
    </xsl:if>
    <a href="/">Home</a>
    </li>
    </ul>
    <xsl:call-template name="buildNav">
    <xsl:with-param name="node" select="$currentPage/ancestor-or-self::node [@level = 2]"/>
    <xsl:with-param name="id" select="string('treemenu1')"/>
    </xsl:call-template>
    </xsl:template>

    <xsl:template name="buildNav">
    <xsl:param name="node"/>
    <xsl:param name="id"/>

    <xsl:if test="$node/node [string(./data [@alias='umbracoNaviHide']) != '1']">
    <ul>
    <xsl:if test="$id != ''">
    <xsl:attribute name="id">
    <xsl:value-of select="$id"/>
    </xsl:attribute>
    </xsl:if>
    <xsl:for-each select="$node/node [string(./data [@alias='umbracoNaviHide']) != '1']">
    <li>
    <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
    <xsl:attribute name="class">current</xsl:attribute>
    </xsl:if>
    <a href="{umbraco.library:NiceUrl(@id)}">
    <xsl:attribute name="title">
    <xsl:value-of select="@nodeName"/>
    </xsl:attribute>
    <xsl:value-of select="@nodeName"/>
    </a>
    <xsl:if test="count(./node) &gt; 0">
    <xsl:call-template name="buildNav">
    <xsl:with-param name="node" select="."/>
    </xsl:call-template>
    </xsl:if>
    </li>
    </xsl:for-each>
    </ul>
    </xsl:if>
    </xsl:template>
    </xsl:stylesheet>

     

    You might wish to add a check to see which level you are at and limit the amount of recursion if for example you have site content 3 layers deep but you only wish to show the first two in your main navigation.

    And for the CSS

    ul#nav li
    {
    Your top level CSS goes here
    }
    ul#nav li ul li
    {
    Your second level CSS goes here... I think you'll get the idea for the third level :)
    }

    I hope this helps :)

  • Jan Itor 3 posts 23 karma points
    Oct 28, 2009 @ 15:23
    Jan Itor
    0

    Thanks for your reply, Chris.  I am still a little unfamiliar with XSLT, so how would I code in logic to only display third level child nodes to display only when their direct parent node is selected?

  • Chris Houston 535 posts 980 karma points MVP admin c-trib
    Oct 28, 2009 @ 15:56
    Chris Houston
    0

    Hi,

    You could modify my above code and just add a of xsl:choose statement. I have not tested this and just written it directly into the forum so appologies for the formatting and if there is a bug! I think it is right though and should get you in the right direction :)

    <?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="xml" omit-xml-declaration="yes" />
     
    <xsl:param name="currentPage"/>
     
     
    <xsl:template match="/">
     
    <ul id="nav">
       
    <li>
         
    <xsl:if test="$currentPage/@nodeName = 'Home'">
           
    <xsl:attribute name="class">current</xsl:attribute>
         
    </xsl:if>
         
    <a href="/">Home</a>
       
    </li>
     
    </ul>
     
    <xsl:call-template name="buildNav">
       
    <xsl:with-param name="node" select="$currentPage/ancestor-or-self::node [@level = 2]"/>
       
    <xsl:with-param name="id" select="string('treemenu1')"/>
     
    </xsl:call-template>
     
    </xsl:template>

     
    <xsl:template name="buildNav">
       
    <xsl:param name="node"/>
       
    <xsl:param name="id"/>

       
    <xsl:if test="$node/node [string(./data [@alias='umbracoNaviHide']) != '1']">
         
    <ul>
           
    <xsl:if test="$id != ''">
             
    <xsl:attribute name="id">
               
    <xsl:value-of select="$id"/>
             
    </xsl:attribute>
           
    </xsl:if>
           
    <xsl:for-each select="$node/node [string(./data [@alias='umbracoNaviHide']) != '1']">
    <xsl:choose>
    <xsl:when test="current()/@level = 3 and current()/parent::node/@id != $currentPage/@id">

    <!-- Do nothing - You might want to change this to greater or equal to 3... -->

    </xsl:when>
    <xsl:otherwise>

              <li>
               
    <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
                 
    <xsl:attribute name="class">current</xsl:attribute>
               
    </xsl:if>
               
    <a href="{umbraco.library:NiceUrl(@id)}">
                 
    <xsl:attribute name="title">
                   
    <xsl:value-of select="@nodeName"/>
                 
    </xsl:attribute>
                 
    <xsl:value-of select="@nodeName"/>
               
    </a>
               
    <xsl:if test="count(./node) &gt; 0">
                 
    <xsl:call-template name="buildNav">
                   
    <xsl:with-param name="node" select="."/>
                 
    </xsl:call-template>
               
    </xsl:if>
             
    </li>
    </xsl:otherwise>
    </xsl:choose>
            </xsl:for-each>
         
    </ul>
       
    </xsl:if>
     
    </xsl:template>
    </xsl:stylesheet>

     

  • Jan Itor 3 posts 23 karma points
    Oct 28, 2009 @ 17:16
    Jan Itor
    0

    Thanks, Chris! 

    I've debugged your code and cannot figure out why on any level 1 node that the first level 2 node shows correcly, but every subsequent level 2 node show up nested beneth the first level 2 node.  However, when I navigate to a level 2 node, every <li> nests itself in the <ul> as it should.

    Sorry to bother you so much, but XSLT is still a bit hazy for me.  Throw in a round of recursion, and my head starts spinning.

  • dandrayne 1138 posts 2262 karma points
    Oct 28, 2009 @ 17:34
    dandrayne
    0

    Hows about showing and hiding the subnav with a tiny bit of css?

    ul#nav li ul { display: none }
    ul#nav li.current ul { display: block }

    Dan

Please Sign in or register to post replies

Write your reply to:

Draft