Copied to clipboard

Flag this post as spam?

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


  • Emil Tinebo 19 posts 39 karma points
    Aug 14, 2010 @ 17:12
    Emil Tinebo
    0

    multi level menu navigation with top level anchor

    Hello my fellow Umbracoholics,

    I'm stuck in my own way of thinking when trying to create a multilevel navigation menu.

    I want the final result to behave like this:

    Home (clickable, takes us to the home page)
    Page 1 (Anchor link (#), only child nodes takes us further)
       -- Page 1a (clickable, takes us to Page 1a)
       -- Page 1b (clickable, takes us to Page 1b)
    Page 2 (Anchor link (#))
      -- Page 2a (clickable)
      -- Page 2b (clickable)
    Page 3 (clickable, takes us to Page 3)
    Page 4 (clikcable, takes us to Page 4)

    So what I want to do is basically to check if a node has any child nodes. If it does, render a link to # instead of the child's NiceUrl.

    If a node doesn't have any child nodes, render a link to the nodes NiceUrl.

    The code I have is as follows:

    <?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" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets"
      exclude-result-prefixes="msxml
    umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes
    Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings
    Exslt.ExsltSets "
    >

    <xsl:output method="xml" omit-xml-declaration="yes"/>

    <xsl:param name="currentPage"/>

    <xsl:variable name="maxLevelForSitemap" select="4"/>

    <xsl:template match="/">
    <xsl:call-template name="drawNodes">  
    <xsl:with-param name="parent" select="$currentPage/ancestor-or-self::* [@isDoc and @level=1]"/>  
    </xsl:call-template>
    </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="sf-menu"><xsl:for-each select="$parent/* [@isDoc and string(umbracoNaviHide) != '1' and @level &lt;= $maxLevelForSitemap]">
    <li>
          <xsl:if test="$currentPage/@id = current()/@id">
          <xsl:attribute name="class">current</xsl:attribute>
          </xsl:if>
          <xsl:choose>
            <xsl:when test="$parent/@level = '1' and count(descendant::*) &lt; 1">
              <a href="#">
              <xsl:value-of select="@nodeName"/></a>
            </xsl:when>
            <xsl:otherwise>
              <a href="{umbraco.library:NiceUrl(@id)}">
              <xsl:value-of select="@nodeName"/></a>
            </xsl:otherwise>
      </xsl:choose>
      <xsl:if test="count(./* [@isDoc and string(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>

    But it renders a link to every page. I'm sure the solution is simple but I cannot for my life figure out what I'm missing.

    Any suggestions?

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Aug 14, 2010 @ 23:54
    Chriztian Steinmeier
    1

    Hi Emil,

    It's really not that tricky - starting from scratch, here's how I'd do it:

    <?xml version="1.0" encoding="utf-8" ?>
    <xsl:stylesheet
        version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:umbraco.library="urn:umbraco.library"
        exclude-result-prefixes="umbraco.library"
    >
    
        <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
    
        <xsl:param name="currentPage" select="/root/Website/Textpage[4]"/>
        <xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::*[@level = 1]" />
    
        <xsl:template match="/">
            <ul>
                <li><a href="/">Home</a></li>
                <xsl:apply-templates select="$siteRoot/*[@isDoc]" />
            </ul>
        </xsl:template>
    
        <xsl:template match="*[@isDoc]">
            <li>
                <a href="{umbraco.library:NiceUrl(@id)}">
                    <xsl:if test="*[@isDoc][not(umbracoNaviHide = 1)]">
                        <xsl:attribute name="href">#</xsl:attribute>
                    </xsl:if>
    
                    <xsl:value-of select="@nodeName" />
                </a>
    
                <xsl:if test="*[@isDoc][not(umbracoNaviHide = 1)]">
                    <ul>
                        <xsl:apply-templates select="*[@isDoc]" />
                    </ul>
                </xsl:if>
            </li>
        </xsl:template>
    
        <!-- Never output these -->
        <xsl:template match="*[umbracoNaviHide = 1]" />
    
    </xsl:stylesheet>
    
    The only 'trick' here is knowing that XSLT will not generate duplicate attributes, so first we put the standard NiceUrl() link in the href attribute, and then we check for child nodes - if present, we redefine the href attribute by putting a hash in there.
    (If you need the security stuff from the "drawNodes" version, you should be able to add them back in.)

    /Chriztian 

  • BarneyHall 141 posts 210 karma points
    Aug 24, 2010 @ 23:58
    BarneyHall
    0

    Got a question...

    How does the "/root/Website/Textpage[4]" expression work? What's it referencing? Never seen this sort of thing before in other Umbraco XSLT.

    I like the look of this, seems pretty efficient to me, just wish my dumb brain could understand what's going on :)

    Thanks,
    Barney

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Aug 25, 2010 @ 01:18
    Chriztian Steinmeier
    0

    Hi Barney - well spotted :-)

    That expression is actually only used when developing locally - I create an XML file to mimic the Umbraco XML when answering posts like these, and then I use the select attribute on the param element to "fake" the $currentPage parameter when transforming locally. Though I usually delete it again before posting, it's safe to leave it in because it's only used as a default value if none gets assigned (which Umbraco does upon transformation).

    So in this specific instance, I've faked a "Website" document at level 1 with at least 4 "Textpage" documents below it - the expression selects the 4th of those as $currentPage.

    It's so much faster to do it this way, than having to log in to an Umbraco site and do the tweaking.

    /Chriztian

  • BarneyHall 141 posts 210 karma points
    Aug 25, 2010 @ 15:11
    BarneyHall
    0

    Cheers Chriztian, I understand now.

    Got one more question for you though :)

    How could I stop it replacing the href with # on parent nodes beyond the 2nd level nodes?

    I thought something like this might do it, but doesn't work and it's bending my tiny brain...

                    <xsl:if test="*[@isDoc][not(umbracoNaviHide = 1)][not(@level &gt; 2)]">
                        <xsl:attribute name="href">#</xsl:attribute>
                    </xsl:if>

    Thanks,
    Barney

  • BarneyHall 141 posts 210 karma points
    Aug 25, 2010 @ 15:15
    BarneyHall
    0

     

    Actually just got it working with this... but please tell me if you know a tidier way of doing:

                    <xsl:if test="*[@isDoc][not(umbracoNaviHide = 1)] and current()[@level = 2]">
                        <xsl:attribute name="href">#</xsl:attribute>
                    </xsl:if>
Please Sign in or register to post replies

Write your reply to:

Draft