Re: [xsl] alternate bgcolor when node attribute changed

Subject: Re: [xsl] alternate bgcolor when node attribute changed
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Mon, 23 Jul 2001 11:18:04 +0100
Hi Nelli,

> with the first one I still have a problem (just wanted to understand
> better all these things): it returns only 1 row though i call the
> template, what's wrong here? could you look please?

Sure. There's one problem that's the actual reason behind why it's not
working, and I suspect another problem to do with your understanding
of how XSLT processing works. So first things first - look at the
xsl:apply-templates in the following:

> <xsl:template match="request">
>         <xsl:param name="previous-bgcolor" />
>         ...
>         <TR bgcolor="{$bgcolor}">
>                 <!--<TD></TD> elements-->
>         </TR>
>                 <xsl:if test="position()!=last()">
>                         <xsl:apply-templates
> select="/requests/request[position()+1]">
>                                 <xsl:with-param name="previous-bgcolor"
select="$bgcolor" />>
>                         </xsl:apply-templates>
>                 </xsl:if>
>         </xsl:template> 

The path is '/requests/request[position() + 1]'. This gets all the
request element children of the requests document element and then
filters them according to the predicate. Within the predicate, the
context node (which is used by the position() function) is the request
element that you're filtering. You take the position() of that request
element (so for the first that will be 1, the second 2 and so on), and
add 1 to it. Because this expression results in a number, then the
request element is filtered in if its position is equal to the result
of this addition. The XPath is equivalent to:

  /requests/request[position() = (position() + 1)]

As you can probably see, this predicate is never true, so the
expression never selects any request elements.

I think that what you were trying to do was find the request element
whose position was the position of the *current* request element (the
one you're processing in the template), plus one. In that case, you

  <xsl:variable name="position" select="position()" />
  <xsl:apply-templates select="/requests/request[$position + 1]">
    <xsl:with-param name="previous-bgcolor" select="$bgcolor" />

However, this won't do what you want it to do either.

The xsl:apply-templates instruction gathers all the nodes that you
select into a node set and then tries to find templates to apply to
them. This node set is ordered according to document order (as long as
you don't override it with an xsl:sort). The sorted set of nodes
becomes the "current node list". As the processor steps through the
current node list, each node that it looks at becomes the current node
within the template that matches it, and the current node has a
position within the current node list, which you can get with the
position() function.

In the first run of your template, the request element is the first
request element, and has a position() of 1. In the
xsl:apply-templates, then, you create a node set containing a single
node, the second request (/requests/request[1 + 1]). This node set
becomes the current node list and the second request becomes the
current node in the following template. However, it's the only node in
the current node list, so it obviously has a position() of 1. Thus,
when the template is applied to this second request element, the
xsl:apply-templates again creates a node set containing a single node,
the second request. And the cycle repeats endlessly.

The moral is that in XSLT it tends to be hard work to use the
position() of a node to iterate through a number of nodes. Instead,
there's lots of support for moving from node to node directly, without
having to use any indexes. For example, in the solution that I gave
you, the xsl:apply-templates within the template here actually looked

  <xsl:apply-templates select="following-sibling::request[1]">
    <xsl:with-param name="previous-bgcolor" select="$bgcolor" />

This uses the tree relationship between the request elements to work
out what the next request element to process is.

Of course if you just like using index numbers, then you can use them
- use a parameter to pass the request number from one step to the
next, and use that in the predicate to select the relevant request

I hope that helps,


Jeni Tennison

 XSL-List info and archive:

Current Thread