How Do You Iterate Nodes with xsl:for-each in XSLT?
This article examines how the <xsl:for-each>
element processes node-sets in Extensible Stylesheet Language
Transformations (XSLT). It explains how the processor evaluates the
target node-set, manages context shifts during traversal, applies
sorting rules, and constructs the resulting output tree.
The Mechanism of
<xsl:for-each>
The <xsl:for-each> instruction directs an XSLT
processor to iterate over a sequence of nodes selected by an XPath
expression. Unlike imperative programming loops (such as
for or while), which evaluate state changes
sequentially across iterations, <xsl:for-each> acts
as a declarative iteration pattern across an immutable node-set.
When the processor encounters
<xsl:for-each select="expression">, it performs the
following steps:
- Evaluation: The processor evaluates the XPath
expression in the
selectattribute against the current context node to generate a node-set (or sequence in XSLT 2.0+). - Context Establishment: The processor establishes a current node list containing all matched nodes, arranged by default in document order.
- Execution: The processor loops through the node list, applying the inner template body to each node individually.
Context Switching Inside the Loop
During each iteration of <xsl:for-each>, the
processor automatically alters the evaluation context:
- Current Node (
.): The node currently being processed becomes the context node. All relative XPath expressions evaluated within the body of the loop originate from this node. - Context Position (
position()): Returns the 1-based index of the current node within the iteration list. - Context Size (
last()): Returns the total count of nodes contained in the selected node-set.
Example XML Source
<catalog>
<book id="bk101">
<title>XML Guide</title>
<price>44.95</price>
</book>
<book id="bk102">
<title>XPath Basics</title>
<price>29.99</price>
</book>
</catalog>Example Transformation
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/catalog">
<ul>
<xsl:for-each select="book">
<li>
<xsl:value-of select="position()"/>:
<xsl:value-of select="title"/> -
$<xsl:value-of select="price"/>
</li>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>In this transformation, the expression select="book"
yields a node-set containing both <book> elements.
Within the loop body, select="title" and
select="price" evaluate relative to each active
<book> node rather than the root
<catalog>.
Sorting Iteration Output
By default, <xsl:for-each> traverses nodes in
document order. You can override this sequence by inserting one or more
<xsl:sort> child elements immediately inside the
<xsl:for-each> block before any template content.
<xsl:for-each select="book">
<xsl:sort select="price" data-type="number" order="ascending"/>
<li>
<xsl:value-of select="title"/> (<xsl:value-of select="price"/>)
</li>
</xsl:for-each>When <xsl:sort> is present, the processor reorders
the node list before beginning iteration. Consequently,
position() reflects the sorted rank rather than the
original document position.
<xsl:for-each>
Versus <xsl:apply-templates>
While <xsl:for-each> provides a compact way to
handle repetitive structures, it tightly couples template logic to a
specific document hierarchy. In standard XSLT design,
<xsl:apply-templates> is preferred for recursive or
polymorphic XML structures, while <xsl:for-each> is
best suited for tabular data rendering and straightforward, localized
iterations.