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']) > 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:
<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" 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']) > 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.