Generating Tables of ContentsA Table of Contents is a nice addition to a web article, and also a great application
for our framework. When done manually, TOCs are hard to create and
maintain. They get quickly out of sync with the article's content and
are very error prone. This is where XSLT comes to help. A XSLT Table of Contents will automatically extract the entry
text information (section
titles), keep it updated, establish the indentation and number
the sections. It should look something like this: - 1 First Section
- 1.1 First Subsection of the first section
- 1.2 Second Subsection of the first section
- 2 Second Section
The XML SchemaWe will start with some modifications to the existing schema
definitions. The section type
First of all, we need to allow toc elements in the article
sections. The toc elements will be block-level elements, just like
the paragraphs. They are optional and can appear in any order,
number and position. We'll modify the section type as follows: ...
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="toc" type="tocType"/>
<xs:element name="paragraph" type="xs:string"/>
</xs:choice>
...
The toc type
The toc element only has two attributes: - global - a true/false boolean
- style - of type list-style
<xs:complexType name="tocType">
<xs:attribute name="style" type="list-styleType" default="unordered"/>
<xs:attribute name="global" type="xs:boolean" default="false"/>
</xs:complexType>
The global toc attributeThe Table of Contents can be: - global - per the entire article
- local (default) - includes only the sections contained in
the parent section of the toc element
The list-style
type
The list-style is an enumeration establishing how a
hierarchical list should be created in HTML. A TOC is such a list. There are three types of lists: - ordered - the OL tag will be used to create the list
- unordered (default) - the UL tag will be used to create the
list
- numbered - the UL tag will be used to create the list, but
the entries will be numbered using the same numbering abilities
as when numbering section titles.
<xs:simpleType name="list-styleType">
<xs:restriction base="xs:string">
<xs:enumeration value="unordered"/>
<xs:enumeration value="ordered"/>
<xs:enumeration value="numbered"/>
</xs:restriction>
</xs:simpleType>
Hiding the UL bullets for numbered lists gives a true TOC
numbering "1.1." styles. The Website XML contentIntegrating the TOC in the article is very simple: <section>
<title>Table of Contents</title>
<toc style="numbered" global="true"/>
</section>
The XSLT processing rulesWe need to match and process the toc element, taking into
consideration the set attributes. The toc templateThis template will be called for each toc element. It will
prepare the parameters and call the do-toc template. <xsl:template match="toc">
<xsl:call-template name="do-toc">
<xsl:with-param name="entires"
select="if (@global='true') then ancestor::article/section[title] else ../section[title]"/>
<xsl:with-param name="element"
select="if (@style='ordered') then 'ol' else 'ul'" tunnel="yes"/>
<xsl:with-param name="numbered" select="@style='numbered'" tunnel="yes"/>
<xsl:with-param name="global" select="@global='true'" tunnel="yes"/>
</xsl:call-template>
</xsl:template>
The entries parameter is the set of section elements which
are recursively iterated and placed in the TOC. The initial entries
set depends on if the TOC is global or local: - global TOC - the initial entry set represents all the titled
sections included in the enclosing article.
- local TOC - the initial entry set includes all the sibling
titled section elements
The do-toc templateThis template will be recursively called for each set of
TOC entries. If the set is not empty, the list tags are outputted.
Then, for each entry in the set: - a TOC item is outputted
- a new entry set is constructed, containing all the child
titled sections of the current section
- the do-toc template is called on the new set
<xsl:template name="do-toc">
<xsl:param name="entires"/>
<xsl:param name="element" tunnel="yes"/>
<xsl:param name="numbered" tunnel="yes"/>
<xsl:if test="$entires">
<xsl:element name="{$element}">
<xsl:for-each select="$entires">
<li>
<xsl:call-template name="title-number"/>
<xsl:value-of select="title"/>
<xsl:call-template name="do-toc">
<xsl:with-param name="entires" select="section[title]"/>
</xsl:call-template>
</li>
</xsl:for-each>
</xsl:element>
</xsl:if>
</xsl:template>
The title-number templateThis template numbers entries just like section titles. The
numbering is turned on by the sheet parameter $numbered-sections
because we should have TOC numbering whenever we have title
numbering. Otherwise, it is turned on by the numbered list style
type. When numbering because we have numbered sections, the
numbered-first-section sheet parameter controls if the outer most
section is counted. When opting for a numbered style, a global TOC causes us to
number all the sections, while a local one only numbers title
sections from the current level down. <xsl:template name="title-number">
<xsl:param name="numbered" tunnel="yes"/>
<xsl:param name="global" tunnel="yes"/>
<xsl:choose>
<xsl:when test="$numbered-sections">
<xsl:choose>
<xsl:when test="$numbered-first-section">
<xsl:number level="multiple" count="section" format="1.1 "/>
</xsl:when>
<xsl:otherwise>
<xsl:number level="multiple" count="section/section" format="1.1 "/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$numbered">
<xsl:choose>
<xsl:when test="$global">
<xsl:number level="multiple" count="section" format="1.1 "/>
</xsl:when>
<xsl:otherwise>
<xsl:number level="multiple" count="section[toc]//section"
format="1.1 "/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:template>
Warning: This strategy fails if we have a toc element in a section
that is descendant of another toc-element-holding section. But we
shouldn't have that anyway - that type of overlapping TOC constructs
don't make much sense in a single web page. The title templateIt only makes sense to use this new, more powerful
numbering template for the section titles as well. ...
<xsl:element name="h{$heading}">
<xsl:call-template name="title-number"/>
<xsl:apply-templates/>
</xsl:element>
...
The HTML outputThe generated TOC should look something like this: <h2>Table of Contents</h2>
<ul>
<li>1 Main Section
<ul>
<li>1.1 Table of Contents</li>
<li>1.2 Sub-Section 1
<ul>
<li>1.2.1 Sub-Section 1.1
<ul>
...
Further developmentAdditional features we will implement for Table of Contents
when we have the link templates: - links from the TOC entries to the respective section titles
- use the absence/presence of the anchor (section id) as TOC
section inclusion criteria
Download: article files. Read on: Links in Tables of Contents. First Posted: December 19th, 2005 - Monday. Last Updated: January 18th, 2006 - Wednesday. |