Rate this page

Flattr this

Create a list of numbered footnotes using XSLT

Tested on

Debian (Lenny, Squeeze)
Ubuntu (Lucid, Precise, Trusty)

Objective

To create a list of automatically numbered footnotes using XSLT, with matching footnote markers in the text and next to each footnote.

Scenario

Suppose that you want to add support for automatically-numbered footnotes to an XSLT stylesheet.

In the input document, each footnote is represented by a footnote element that contains the text of the footnote and is placed at the point in the document to which it refers, for example:

<p>Three is the number to which you will count.<footnote>Armaments 2:9-21</footnote></p>

In the output document, the footnotes should be moved to a list at the end of the page. Each footnote should leave behind a superscripted footnote marker of the form [n] at the location on the page to which it refers. There should be a matching footnote marker next to the relevant footnote, for example:

Three is the number to which you will count.[1]

[1] Armaments 2:9-21

Matching footnote markers should be hyperlinked to each other. The list of footnotes should be represented by an ol element of class footnote, and each member of that list should be an li element of class footnote.

(The element types ol and li have been chosen because they are semantically correct. Their default style is unlikely to be appropriate, but this can easily be changed using a suitable CSS stylesheet.)

Method

Overview

The method described here has three steps:

  1. When they are first encountered, transform each footnote element into a numbered footnote marker.
  2. At the end of the page body, perform a second pass through the input document searching for footnote elements.
  3. During the second pass, transform each footnote element into a footnote marker and the corresponding text.

Transform each footnote element into a numbered footnote marker

At the point in the text where the footnote appears in the input document, it should appear only as a footnote marker in the output document. The template for a footnote element should not therefore include an apply-templates instruction, but instead contain the markup needed to construct a footnote marker.

Numbering is performed using the xsl:number instruction to count the number of footnote elements in the input document up to and including the current one:

<xsl:number level="any" count="footnote" format="[1]"/>

The level attribute specifies that the elements counted need not be siblings of the current footnote element, but rather, can occur at any level of the input document (higher or lower). The count attribute specifies that only footnote elements are to be counted. The format attribute specifies that the value should be written to the output document as a decimal number in square brackets. (The brackets could have been added later, but it is slightly neater to generate them here.)

To perform the hyperlinking it is necessary to choose an anchor names for each footnote marker. Every anchor name in the document must be unique, and the easiest way to achieve this is by incorporating the footnote number into the name. The scheme that will be used here is as follows, where N is the footnote number:

The numbers are generated using the same method as before, except that instead of being written to the output document as text it must become part of the value of either a name or an href attribute. This is done using an xsl:attribute instruction, which converts its content into an attribute value.

The resulting template as as follows:

<xsl:template match="footnote">
 <a>
  <xsl:attribute name="name">
   <xsl:text>footnoteref</xsl:text><xsl:number level="any" count="footnote" format="1"/>
  </xsl:attribute>
  <xsl:attribute name="href">
   <xsl:text>#footnote</xsl:text><xsl:number level="any" count="footnote" format="1"/>
  </xsl:attribute>
  <sup><xsl:number level="any" count="footnote" format="[1]"/></sup>
 </a>
</xsl:template>

Perform a second pass through the input document searching for footnote elements

By default the xsl:apply-templates instruction acts on the content of the current node from the input document, however it can be instructed to act on the result of any XPath expression. This can select any subset of the input document, including parts that have already been processed.

In this case the requirement is to select all footnote elements wherever they occur. This can be done using the XPath expression ‘//footnote’, meaning elements of type footnote that are descended from the root node.

Here is an example of how the relevant instructions could be incorporated into a template for an HTML-style body element:

<xsl:template match="body">
 <body>
  <xsl:apply-templates/>
  <ol class="footnotes">
   <xsl:apply-templates select="//footnote" mode="footnote"/>
  </ol>
 </body>
</xsl:template>

Alternatively, it may be preferable for the source document to explicitly indicate where the list of footnotes should be inserted.

The mode attribute of the apply-templates instruction is explained below.

Transform each footnote element into a footnote marker and the corresponding text

During the second pass the footnote elements will be transformed again, but the result must be different from that obtained during the first pass. Instead of just a footnote marker, the entire footnote must be generated as the content of a list item.

XSLT allows this to be done by passing a mode argument to the relevant template and apply-templates instructions. In this example the mode has been named footnote:

<xsl:template match="footnote" mode="footnote">
 <li>
  <a>
   <xsl:attribute name="name">
    <xsl:text>footnote</xsl:text><xsl:number level="any" count="footnote" format="1"/>
   </xsl:attribute>
   <xsl:attribute name="href">
    <xsl:text>#footnoteref</xsl:text><xsl:number level="any" count="footnote" format="1"/>
   </xsl:attribute>
   <xsl:number level="any" count="footnote" format="[1]"/>
  </a>
  <xsl:text> </xsl:text>
  <xsl:apply-templates/>
 </li>
</xsl:template>

As previously, the xsl:number instruction has been used to number both the footnote marker and the hyperlink anchors. The footnote body is output using an ordinary xsl:apply-templates instruction so that any stylistic markup within it is appropriately transformed.

Tags: xslt