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.