Creating a Table of Contents

Often an element from the source XML document appears multiple times in the output, with different formatting in each case. The classic example is the table of contents, in which section titles appear at the start of the document, and each one is also displayed at the start of the section. XSLT makes it easy to place something in multiple places—just select it multiple times using the <xsl:for-each> or <xsl:apply-templates> elements.

The <xsl:for-each> element provides a simple way to apply different formatting for each location. The <xsl:for-each> element contains its own template, so different output for various occurrences of an element is implied. In the following example, the title elements are pulled from the top-level section and used in a simple table of contents.

<xsl:template match="/">
  <H4>Table of Contents</H4>
  <xsl:for-each select="document/section">
    <DIV STYLE="margin-left:1em">
      <xsl:value-of select="title"/>
    </DIV>
  </xsl:for-each>
  <xsl:apply-templates select="document/section"/>
</xsl:template>
<xsl:template match="section">
  <DIV>
    <H2><xsl:value-of select="title"/></H2>
    <xsl:apply-templates />
  </DIV>
</xsl:template>

This example is a simplification of that found in the To The Pole Sample, with table of contents.

Often, however, a hierarchical table of contents is needed. Typically this is the case when the document itself defines sections recursively, for example sections which generate subsections, which in turn generate further subsections. <xsl:apply-templates> is suited for recursive structure, and allows templates to be defined locally. A template scoped in this way will not participate in the selection of an appropriate template at the global level. The template will only apply for items placed in the table of contents.

A document grammar with recursive structure is used by the Worldwide Web Consortium (W3C) for XML versions of recommendations, such as the XSLT Transformation. This grammar contains nested sections named "div1", "div2", and so on.

The following example shows a hierarchical table of contents for this grammar. It uses an <xsl:template> element contained in, or scoped to, the <xsl:apply-templates> element, which selects the top-level sections, "div1" in the document body.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="text()"/>

<xsl:template match="/">
  <html>
    <body>
      <H3>Table of Contents</H3>
      <xsl:apply-templates select="spec/body/div1 | spec/back/*" mode="toc"/>
    </body>
  </html>
</xsl:template>

<xsl:template match="div1|div2|div3|div4|div5|div6" mode="toc">
  <DIV STYLE="margin-left:1em"><xsl:number level="multiple" format="1.1.1.1.1. " count="div1|div2|div3|div4|div5|div6"/>
  <xsl:value-of select="head"/><br/>
  <xsl:apply-templates select="div2|div3|div4|div5|div6" mode="toc"/>
  </DIV>
</xsl:template>

<xsl:template match="inform-div1" mode="toc">
  <DIV STYLE="margin-left:1em"><xsl:number level="multiple" format="1.1.1.1.1. "/>
    <xsl:value-of select="head"/> (Non-Normative)
    <xsl:apply-templates select="div2" mode="toc"/>
  </DIV>
</xsl:template>
</xsl:stylesheet>

The local templates are tested for a match before looking for a global template, and build nested <DIV> elements to indent each level in relation to its parent. Because <xsl:apply-templates> is careful to select only elements that can be matched by local templates, there is no possibility of interfering with the operation of other templates in the style sheet.

See Also

Advanced XSLT Features

 Last updated on Saturday, April 10, 2004

© 1992-2003 Microsoft Corporation. All rights reserved.