joe
joe

Reputation: 17478

How to get position of parent element - XSL

What I wish I could do in xsl is the following, but unfortunatly parent/position() is not valid.

XSL

<xsl:template match="li">
  <bullet>
    <xsl:apply-templates/>
  </bullet>
  <!-- if this is the last bullet AND there are no more "p" tags, output footer -->
  <xsl:if test="count(ancestor::div/*) = parent/position()">
    <footer/>
  </xsl:if>
</xsl:template>

XML

<html>
  <div>
    <p>There is an x number of me</p>
    <p>There is an x number of me</p>
    <p>There is an x number of me</p>
    <ul>
      <li>list item</li>
      <li>list item</li>
      <li>list item</li>
      <li>list item</li>
      <li>list item</li>
    </ul>
  </div>
</html>

Anyone have any ideas how to figure out this problem from WITHIN my template match for li?

Thanks!

Upvotes: 1

Views: 11380

Answers (4)

newtover
newtover

Reputation: 32094

Try this one:

<xsl:template match="li">
  <bullet>
    <xsl:apply-templates/>
  </bullet>
  <xsl:if test="position()=last() and not(../following-sibling::p)">
    <footer/>
  </xsl:if>
</xsl:template>

Upvotes: 1

markusk
markusk

Reputation: 6667

You can find the position of the parent node in the source tree by counting its preceding siblings:

<xsl:variable name="parent-position" 
              select="count(../preceding-sibling::*) + 1"/>

If you want to determine if there are any p elements after the parent ul element, you can test this without using positions at all:

<xsl:if test="../following-sibling:p">...</xsl:test>

However, as Dimitre and Oliver have noted, it is more in the spirit of XSLT to add the footer when processing the parent element. Also, the XPath expressions shown only care about order in the original source tree. If you intend to filter elements or reorder with xsl:sort before processing, these paths will not work as desired, as they will look at the original ordering and include all nodes in the source tree.

Upvotes: 9

Oliver Hallam
Oliver Hallam

Reputation: 4262

If I understand correctly you are looking for the last li; that is an li with no li elements following it. This can be tested as so:

<xsl:template match="li">
  <bullet>
    <xsl:apply-templates/>
  </bullet>
  <xsl:if test="not(following-sibling::li)">
    <footer />
  </xsl:if>
</xsl:template>

Although in the case you have given it seems to be more in the spirit of XSLT to add the footer when processing the end of the ul:

<xsl:template match="ul">
  <ul>
    <xsl:apply-templates/>
  </ul>
  <footer />
</xsl:template>

<xsl:template match="li">
  <bullet>
    <xsl:apply-templates/>
  </bullet>
</xsl:template>

Upvotes: 0

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243449

A good way to do this in XSLT is:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="div">
  <xsl:apply-templates/>
  <footer/>
 </xsl:template>

 <xsl:template match="li">
  <bullet>
    <xsl:apply-templates/>
  </bullet>
 </xsl:template>
</xsl:stylesheet>

The inclusion of the <footer/> is most natural at the end of the template that matches div and there is no need to try to compare any positions.

Upvotes: 2

Related Questions