XSLT How To: Reordering and Alternate Styling based on Position
While XSLT is fantastic for organizing and styling XML, when it comes to manipulating the sequence of the information in a way other than basic sorting, it sort of comes up short. Specifically, it seems there isn’t a prepackaged way to grab an item, then simultaneously place it higher in the list and remove it from its original location. This simply cannot be accomplished with the <xsl:sort> element. On top of this, I needed to implement alternate styling, which is easy, except for the point where removing one element causes the alternate styling to at some point become the opposite of what was intended.
The Answer …Well, not yet.
While it’s not the most efficient way to query and arrange the provided XML, I seemingly had no choice but to use multiple <xsl:for-each> statements on the same information. It did however provide the functionality I needed. Only problem was, my alternating style is based on the position() attribute:
<xsl:when test=”position() mod 2 = 0″>
So this all worked fine until the point where I filter out a specific entry. Unfortunately, this does not change the position() of each succeeding entry. I was left with both entries on either side of an entry that was filtered out having the same style. This was not what I was looking for, so I had to implement a switch-case to determine which alternating style to apply based on whether the filtered entry had been reached. So I implemented a global parameter that gets the position() of the entry to be moved, then implemented a switch-case that detects whether or not that entry has been reached. It looks like this:
The Actual Answer
First, establish the position of said entry:
<xsl:param name="PersonID" select="9999" /> <xsl:param name="PersonLocation"> <xsl:for-each select="Root/PersonProfile"> <xsl:if test="ID=$PersonID"> <xsl:value-of select="position()" /> </xsl:if> </xsl:for-each> </xsl:param>
Then pick it out and display it wherever you please:
<xsl:for-each select="Root/PersonProfile"> <xsl:if test="position() = $PersonLocation"> Style the entry here… </xsl:if> </xsl:for-each>
Now, restrict that entry, check whether the current entry is above or below it, and apply the appropriate styling:
<xsl:for-each select="Root/UserProfile"> <xsl:if test="position() != $PersonLocation"> <xsl:if test="position() < $PersonLocation"> <xsl:choose> <xsl:when test="position() mod 2 = 0">
Style 1 here…
</xsl:when> <xsl:otherwise>
Style 2 here…
</xsl:otherwise> </xsl:choose> </xsl:if> <xsl:if test="position() > $PersonLocation"> <xsl:choose> <xsl:when test="position() mod 2 = 0">
Style 2 here…
</xsl:when> <xsl:otherwise>
Style 1 here…
</xsl:otherwise> </xsl:choose> </xsl:if> </xsl:if> </xsl:for-each>
So there you have it. A workaround for manipulating an entry and applying alternate styling that will always display properly.