The XML SiteXSL templates for processing the titles of recursive web page sections

Processing sections titles

We've left the sections, titles and paragraphs in the XML Schema and XSL templates for web articles article in a simple, flat view. However, the whole reason we permitted recursive sections was to be able to show them as such in the generated HTML page. Let's see how.

Headings corresponding to the title level

For each title, we currently use the H1 tag. Instead, we should output headings that correspond to the imbrication level of that section. The first section should use the H1 tag for its title, the second the H2 tag, etc. Let's see how we can do that.

The section template

We will compute the level of the section starting with 1 for the outermost one and incrementing it with each sub-section. The level is passed from one section to its subsections in a tunneled parameter.

  <xsl:template match="section">
    <xsl:param name="level" tunnel="yes">0</xsl:param>
    <xsl:variable name="nextLevel" select="$level + 1"/>
    <xsl:apply-templates>
      <xsl:with-param name="level" select="$nextLevel" tunnel="yes"/>
    </xsl:apply-templates>
  </xsl:template>

The title templates

Now all we have to do in the title template is to retrieve the level and use it when outputting the H tag.

  <xsl:template match="title">
    <xsl:param name="level" tunnel="yes">1</xsl:param>
    <xsl:element name="h{$level}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

The generated HTML

We are now using Hn tags, where n is the imbrication level of the section.

      <h1>Section 1</h1>
      <p>Paragraph</p>
      <h2>Sub-Section 1.1</h2>
      <h3>Sub-Section 1.1.1</h3>
      <p>Text</p>
      <p>Untitled Section 2 Paragraph</p>

Limit headings at 6 levels deep

The trouble is that the HTML specification allows only six H tags: from H1 to H6. So we need to limit our output accordingly, by changing the title template.

    ...
    <xsl:param name="level" tunnel="yes">1</xsl:param>
    <xsl:variable name="heading" select="if ($level &lt; 6) then $level else 6"/>
    <xsl:element name="h{$heading}">
      <xsl:apply-templates/>
    </xsl:element>
    ...

Adding a little more XML test content,

          <section>
            <title>Sub-Section 1.1.1.1 (level 4)</title>
            <section>
              <title>Sub-Section 1.1.1.1.1 (level 5)</title>
              <section>
                <title>Sub-Section 1.1.1.1.1.1 (level 6)</title>
                <section>
                  <title>Sub-Section 1.1.1.1.1.1.1 (level 7)</title>
                </section>
              </section>
            </section>
          </section>

shows that the title for the sections more than 6 levels deep are all using the H6 tag:

      <h4>Sub-Section 1.1.1.1 (level 4)</h4>
      <h5>Sub-Section 1.1.1.1.1 (level 5)</h5>
      <h6>Sub-Section 1.1.1.1.1.1 (level 6)</h6>
      <h6>Sub-Section 1.1.1.1.1.1.1 (level 7)</h6>

Numbered titles

A nice-to-have feature is the ability to automatically number the section titles according to the section level and section count. Luckily, XSLT provides a very powerful command just for that, called xsl:number.

The trick now is to keep this feature optional and to offer the ability to not number the title of the outer-most section, as that could be seen as a page title. To control the two behaviors we will use two sheet parameters.

  <xsl:param name="numbered-sections"/>
  <xsl:param name="numbered-first-section"/>

And we will change the title template like this:

    ...
    <xsl:element name="h{$heading}">
      <xsl:if 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:if>
      <xsl:apply-templates/>
    </xsl:element>
    ...

When the two parameters are set to true, the output of the transform becomes:

      <h1>1 Section 1</h1>
      <p>Paragraph</p>
      <h2>1.1 Sub-Section 1.1</h2>
      <h3>1.1.1 Sub-Section 1.1.1</h3>
      <p>Text</p>

DIVs around sections

There are many ways other to show embedded subsections. A couple that come to mind are:

  • Surrounding rectangles
  • Indentation

They can both be easily implemented by outputting DIV tags for each section and having CSS display rules for on the tags.

Styled DIVs around sections

Using a sheet parameter to condition this behavior,

  <xsl:param name="section-divs"/>

we externalize the actual section processing in a template named "do-section"

  <xsl:template name="do-section">
    <xsl:param name="level" tunnel="yes">0</xsl:param>
    <xsl:variable name="nextLevel" select="$level + 1"/>
    <xsl:apply-templates>
      <xsl:with-param name="level" select="$nextLevel" tunnel="yes"/>
    </xsl:apply-templates>
  </xsl:template>

which is called in the section template, while conditionally outputting the styled DIV tag:

  <xsl:template match="section">
    <xsl:choose>
      <xsl:when test="$section-divs">
        <div class="section">
          <xsl:call-template name="do-section"/>
        </div>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="do-section"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

The HTML output

With the parameter set to true, the HTML looks like this:

   <body>
      <div class="section">
         <h1>Section 1</h1>
         <p>Paragraph</p>
         <div class="section">
            <h2>Sub-Section 1.1</h2>
            <div class="section">
               <h3>Sub-Section 1.1.1</h3>
               <p>Text</p>
                ...

All that's left to do now is to apply some styles on for the section class and make the sub sections look the way we want.

Wishes

What I would really like is to be able to turn on and off the optional behavior per page. With the sheet parameters I can only control per whole site or sections of the site. But it can work OK if the parts of the site that need the optional features are grouped and processed together in a separate section and transform.

Download: article files.

Read on: Generating Tables of Contents for web articles.

First Posted: December 15th, 2005 - Thursday.
Last Updated: December 19th, 2005 - Monday.