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. XSL makes it easy to place something in multiple placesjust select it multiple times using <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 TOC).
Often, however, a hierarchical table of contents is needed, typically when the document itself defines sections recursivelysections containing subsections ad infinitum. <xsl:apply-templates> is specially suited for recursive structure, and it allows for 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 W3C for XML versions of Working Drafts (such as the Extensible Stylesheet Language ). This grammar contains nested sections named "div1", "div2", and so on. The appendix or back matter can also contain an "inform-div1" that needs some slightly different formatting. While not strictly recursive, it is easy enough to treat it as if it were. The following example shows a hierarchical table of contents for this grammar. It uses an <xsl:template> element contained in (scoped to) the <xsl:apply-templates> element, which selects the top-level sections ("div1" in the document body, "div1" and "inform-div1" in the appendixes).
<H3>Table of Contents</H3> <xsl:apply-templates select="spec/body/div1 | spec/back/*"> <xsl:template match="div1|div2|div3|div4|div5|div6"> <DIV STYLE="margin-left:1em"><xsl:eval>sectionNum(this)</xsl:eval> <xsl:value-of select="head"/> <xsl:apply-templates select="div2|div3|div4|div5|div6"/> </DIV> </xsl:template> <xsl:template match="inform-div1"> <DIV STYLE="margin-left:1em"><xsl:eval>sectionNum(this)</xsl:eval> <xsl:value-of select="head"/> (Non-Normative) <xsl:apply-templates select="div2"/> </DIV> </xsl:template> </xsl:apply-templates>
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. Since <xsl:apply-templates> is careful to only select elements that can be matched by local templates, there is no possibility of interfering with the operation of other templates in the style sheet. Section numbers are added as shown in Generating Item Numbers Using Script.
Try it! The above example is shown in XSL Working Draft Table of Contents Sample.
A variation uses nested definition lists (DL elements) to indent each level.
<H2 CLASS="table-of-contents">Table of Contents</H2> <DL CLASS="table-of-contents"> <xsl:apply-templates select="spec/body/div1"> <xsl:template match="div1|div2|div3"> <DT> <xsl:value-of select="head"/> </DT> <xsl:if test="div2|div3"> <DD> <DL><xsl:apply-templates select="div2|div3"/></DL> </DD> </xsl:if> </xsl:template> </xsl:apply-templates> </DL>
Notice that this version selects only down to "div3" elements. Further subsections indicated by "div4" and beyond are not selected, thus the table of contents is limited to three levels of hierarchy.
Try it! The above example, when augmented by section numbers and links, generates the table of contents of the XSL Working Draft Sample.