Reputation: 1187
I'm working with XSLT 1.0 from PHP and want to wrap all the sibling elements after a heading (h2) into a div so I can toggle them.
The input would look like
...
<h2>Nth title</h2>
<first child>...</first child>
...
<last child>...</last child>
<h2>N+1st title</h2>
...
and the output should be
...
<h2>Nth title</h2>
<div>
<first child>...</first child>
...
<last child>...</last child>
</div>
<h2>N+1st title</h2>
...
Is there a way to do this in XSLT 1.0?
Upvotes: 2
Views: 738
Reputation: 243529
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kFollowing" match="node()[not(self::h2)]"
use="generate-id(preceding-sibling::h2[1])"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="h2">
<xsl:call-template name="identity"/>
<div>
<xsl:apply-templates mode="copy"
select="key('kFollowing', generate-id())"/>
</div>
</xsl:template>
<xsl:template match="node()[not(self::h2)][preceding-sibling::h2]"/>
<xsl:template match="node()" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<html>
<h2>Nth title</h2>
<first-child>...</first-child> ...
<last-child>...</last-child>
<h2>N+1st title</h2> ...
<x/>
<y/>
<z/>
</html>
produces the wanted, correct result:
<html>
<h2>Nth title</h2>
<div>
<first-child>...</first-child> ...
<last-child>...</last-child>
</div>
<h2>N+1st title</h2>
<div> ...
<x></x>
<y></y>
<z></z>
</div>
</html>
Explanation:
The identity rule/template copies every node "as-is".
The identity rule is overriden for h2
elements. Here the action is to copy the h2
element and then to output a div
and inside it to apply templates (in a special mode) to all nodes (that are not h2
themselves) for which this h2
element is the first preceding-sibling h2
element.
The nodes to include in the previous step are conveniently defined as an <xsl:key>
instruction.
In order to stop the nodes that are wrapped in div
to be output again by the identity rule, we provide a template matching such nodes, that simply ignores them.
Upvotes: 2
Reputation: 12824
Yes. Make a template that matches h2 elements; within that template, you can select all following siblings before the next h2 using this xpath expression: following-sibling::*[count(preceding-sibling::h2[1] | current()) = 1]
.
Upvotes: 0