I have a similar problem as this guy:
using position() function in xslt
But I dont need the numbering, I just want to understand the way it works:
<?xml version="1.0" encoding="UTF-8"?>
<test>
<a>blah</a>
<a>blah</a>
<a>blah</a>
<a>blah</a>
</test>
for this input, the following stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="select">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="a">
<xsl:value-of select="position()"/><br/>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
outputs:
<html><body>
2<br>blah
4<br>blah
6<br>blah
8<br>blah
</body></html>
Why does it skip the uneven numbers?
position() Function — Returns a number equal to the context position from the current context.
The position() function gives you the position of the current node within the "current node list", which is whatever was select -ed by the nearest apply-templates or for-each (XSLT 2.0 refers to "items" and "sequences" rather than nodes and node lists but the principle is the same).
Specifies the format pattern. Here are some of the characters used in the formatting pattern: 0 (Digit)
XSLT <xsl:text> The <xsl:text> element is used to write literal text to the output. Tip: This element may contain literal text, entity references, and #PCDATA.
The position()
function gives you the position of the current node within the "current node list", which is whatever was select
-ed by the nearest apply-templates
or for-each
(XSLT 2.0 refers to "items" and "sequences" rather than nodes and node lists but the principle is the same).
In your example the root template applies templates to its two child nodes (the text node containing the line break after the xml declaration, and the test
element). There's no explicit rule matching test
so the default rule applies, which for elements means <xsl:apply-templates/>
.
Thus when the a
template fires its "current node list" is the set of all 9 child nodes of test
. This list consists of alternating text nodes (the line breaks) and element nodes (the a
elements), with the text nodes at the odd positions and the element nodes at the even positions.
If you added an explicit template for test
like this:
<xsl:template match="test">
<xsl:apply-templates select="*"/>
</xsl:template>
Then this would select only the 4 a
element nodes as the current node list, so position()
would give you 1, 2, 3 and 4.
Why does it skip the uneven numbers?
Because while you were at the /
level, you said:
<xsl:apply-templates/>
which applies templates to all nodes children of the root node, and (due to the built-in template rules) to all of their descendants - including the text nodes that separate the <a>
elements.
You will get a different result with an input of:
<?xml version="1.0" encoding="UTF-8"?>
<test><a>blah</a><a>blah</a><a>blah</a><a>blah</a></test>
or if you add an instruction to your stylesheet to:
<xsl:strip-space elements="*"/>
or if you apply templates selectively, e.g.
<xsl:apply-templates select="//a"/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With