Question

I have the following working 100% correctly. However to satisfy my curiosity... is there a way to achieve the same without declaring the currentID variable? Is there some way to reference it from within the Xpath "test" condition?

The xpath query in the condition must refer to 2 @id attributes to see if they match.

  • the 'current' @id
  • each 'ancestor' @id

Here's the code:

<xsl:variable name="currentID" select="@id" />
<xsl:attribute name="class">
<xsl:if test="count($currentPage/ancestor::node [@id = $currentID])&gt;0">descendant-selected </xsl:if>
</xsl:attribute>
Was it helpful?

Solution

Since you select the $currentID from the context node:

<xsl:variable name="currentID" select="@id" />

you can use the current() function, which always refers to the XSLT context node:

<xsl:attribute name="class">
  <xsl:if test="count($currentPage/ancestor::node[@id = current()/@id) &gt; 0]">
    <xsl:text>descendant-selected </xsl:text>
  </xsl:if>
</xsl:attribute>

This way you don't need a variable.

A few other notes:

  • I recommend using <xsl:text> like shown above. This gives you more freedom to format your code and avoid overly long lines.
  • You don't need to do a count() > 0, simply selecting the nodes is sufficient. If none exist, the empty node-set is returned. It always evaluates to false, while non-empty node-sets always evaluate to true.

If you refer to nodes by @id regularly in your XSL stylesheet, an <xsl:key> would become beneficial:

<xsl:key name="kNodeById" match="node" use="@id" />

<!-- ... -->

<xsl:attribute name="class">
  <xsl:if test="key('kNodeById', @id)">
    <xsl:text>descendant-selected </xsl:text>
  </xsl:if>
</xsl:attribute>

The above does not need current() since outside of an XPath predicate, the context is unchanged. Also, I don't count() the nodes, since this is redundant (as explained).

OTHER TIPS

Use current() to refer to the current node processed by the template:

<xsl:if test="count($currentPage/ancestor::node [@id = current()/@id])&gt;0">

Tim got me thinking.... I think I was over complicating things, and I tried the following which works.

<xsl:if test="@id = $currentPage/ancestor::node/@id">descendant-selected </xsl:if>

XSLT seems happy comparing an attribute with a selection of attributes, and evaluating true if any of them match? if anyone has a better explanation of why this works or something better (more succinct) then put it down.

As it has already become clear, referring to the "outer scope" was not an issue, since you could do a direct comparison using the "=" operator. However, there are some cases where you do need current() and more besides where even current() doesn't cut it (because you need to "join" between more than just two contexts). In those cases, XPath 2.0's "for" expressions are indispensable.

You could simply do:

<xsl:if test="count($currentPage[ancestor::node/@id = @id])&gt;0">descendant-selected </xsl:if>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top