HOWTO: Sort Nodes in XSLT

I had an issue with a client's site yesterday: the navigation links weren't showing up in the right order. Despite having used an xsl:for-each, as well as an xsl:sort the order was sorted wrong.

Here is an abstract of the old code:

 

<ul class="priNav">
<ul class="priNav">
  <xsl:for-each select="$currentPage/ancestor-or-self::* [@isDoc and @level=$level]/* [@isDoc and string(showInNavigation) = '1']">
  <xsl:sort select="@sortOrder" order="descending" />
    <li>
      <a href="{umbraco.library:NiceUrl(@id)}">
        <xsl:value-of select="string(menuName)"/>
      </a>
      <xsl:if test="count(./* [@isDoc and string(showInNavigation) = '1']) &gt; 0">
        <ul class="subNav">
          <xsl:for-each select="./* [@isDoc and string(showInNavigation) = '1']">
          <xsl:sort select="@sortOrder" order="descending" />
          <li><a href="{umbraco.library:NiceUrl(@id)}"><xsl:value-of select="string(menuName)"/></a></li>
          </xsl:for-each>
        </ul>
      </xsl:if>
    </li>
  </xsl:for-each>
</ul>

 

 

And here is the updated code that sorts correctly:

 

&lt;ul class="priNav"&gt;
  <ul class="priNav">
  <xsl:for-each select="$currentPage/ancestor-or-self::* [@isDoc and @level=$level]/* [@isDoc and string(showInNavigation) = '1']">
  <xsl:sort select="@sortOrder" order="descending" data-type="number"/>
    <li>
      <a href="{umbraco.library:NiceUrl(@id)}">
        <xsl:value-of select="string(menuName)"/>
      </a>
      <xsl:if test="count(./* [@isDoc and string(showInNavigation) = '1']) &gt; 0">
        <ul class="subNav">
          <xsl:for-each select="./* [@isDoc and string(showInNavigation) = '1']">
          <xsl:sort select="@sortOrder" order="descending" data-type="number"/>
          <li><a href="{umbraco.library:NiceUrl(@id)}"><xsl:value-of select="string(menuName)"/></a></li>
          </xsl:for-each>
        </ul>
      </xsl:if>
    </li>
  </xsl:for-each>
</ul>

 

The magic is the added attribute of data-type to xsl:sort. Previously the nodes sorted based on sortOrder, but used it as a string. Setting the data-type to number made the xslt processor sort the nodes numerically, and resulted in perfect sorting.