问题
Problem: I'm doing a table of contents, with chapters, sections, subsections and subsubsections. What I want is to process the information (that is in XML) with xsl, transforming into an HTML page.
XML:
<chapter id="c1">
<title>Chapter 1</title>
<section id="s1.1">
<title>Motivation</title>
(...)
<section id= "s1.2">
(...)
<chapter id="c2">
<title>Chapter 2 </title>
<section id="s2.1">
<title> Genetics </title>
<subsection id="ss2.1.1">
<title> Brief History </title>
(...)
XSL:
<xsl:template match="toc">
<h3>Table of contents</h3>
<ul>
<xsl:apply-templates mode="indice" select="//chapter">
<xsl:sort/>
</xsl:apply-templates>
</ul>
</xsl:template>
<xsl:template mode="indice" match="chapter">
<h3>
<xsl:value-of select="title"/>
</h3>
<ol>
<xsl:for-each select="section">
<li>
<a href="#{@id}">
<xsl:value-of select="title"/>
</a>
<ol>
<xsl:for-each select="subsection">
<li>
<a href="#{@id}">
<xsl:value-of select="title"/>
</a>
<ol>
<xsl:for-each select="subsubsection">
<li>
<a href="#{@id}">
<xsl:value-of select="title"/>
</a>
</li>
</xsl:for-each>
</ol>
</li>
</xsl:for-each>
</ol>
</li>
</xsl:for-each>
</ol>
</xsl:template>
I've used the following CSS style to make sublists in ordered lists:
<style>
body{
text-align: justify;
}
ol {counter-reset: item}
li {display: block}
li:before {content: counters(item, ".") ". "; counter-increment: item}
</style>
Current Result in HTML:
Chapter 1
1. Motivation
2. Objectives
3. Thesis structure
Chapter 2
1. Genetics
1.1. Brief History
1.2. Topics
2. Genes: study
2.1. Eucariots
2.2. Procaritots
3. Stuff
3.1. stuff
3.2. stuff
3.2.1. stuff
3.2.2. stuff
(...)
Desired Result in HTML:
Chapter 1
1.1. Motivation
1.2. Objectives
1.3. Thesis structure
Chapter 2
2.1. Genetics
2.1.1 Brief History
2.1.2 Topics
2.2. Genes: study
2.2.1. Eucariots
2.2.2. Procaritots
2.3. Stuff
2.3.1. stuff
2.3.2. stuff
2.3.2.1. stuff
2.3.2.2. stuff
(...)
Any suggestions?
回答1:
If you required the numbering to occur in CSS, here is what you can do. With the following XSL :
<xsl:template match="toc">
<h3>Table of contents</h3>
<ul>
<xsl:apply-templates mode="indice" select="//chapter"/>
</ul>
</xsl:template>
<xsl:template mode="indice" match="chapter">
<!-- Add a class on the list items related to a chapter -->
<li class="chapter">
<xsl:value-of select="title"/>
<ol>
<xsl:for-each select="section">
<li>
<a href="#{@id}">
<xsl:value-of select="title"/>
</a>
<xsl:if test="subsection">
<ol>
<xsl:for-each select="subsection">
<li>
<a href="#{@id}">
<xsl:value-of select="title"/>
</a>
<xsl:if test="subsubsection">
<ol>
<xsl:for-each select="subsubsection">
<li>
<a href="#{@id}">
<xsl:value-of select="title"/>
</a>
</li>
</xsl:for-each>
</ol>
</xsl:if>
</li>
</xsl:for-each>
</ol>
</xsl:if>
</li>
</xsl:for-each>
</ol>
</li>
</xsl:template>
and the following CSS :
body {
text-align: justify;
}
ol, ul {
counter-reset: item;
}
li {
display: list-item;
list-style-type: none;
}
li.chapter::before {
content: "";
}
li:before {
content: counters(item, ".") ". ";
counter-increment: item
}
the result is what you expect.
回答2:
Does the counting have to happen in CSS? If not, you could use <xsl:number>
with something like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="text()"/>
<xsl:template match="text()" mode="indice"/>
<xsl:template match="toc">
<html>
<body>
<h3>Table of contents</h3>
<ul>
<xsl:apply-templates mode="indice" select="//chapter">
<xsl:sort/>
</xsl:apply-templates>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="chapter" mode="indice">
<h3>
<xsl:value-of select="title"/>
</h3>
<ul>
<xsl:apply-templates mode="indice"/>
</ul>
</xsl:template>
<xsl:template match="section" mode="indice">
<li>
<xsl:attribute name="data-value">
<xsl:number count="chapter"/>
<xsl:text>.</xsl:text>
<xsl:number count="section"/>
<xsl:text>.</xsl:text>
</xsl:attribute>
<a href="#{@id}">
<xsl:value-of select="title"/>
</a>
<xsl:if test="subsection">
<ul>
<xsl:apply-templates mode="indice"/>
</ul>
</xsl:if>
</li>
</xsl:template>
<xsl:template match="subsection" mode="indice">
<li>
<xsl:attribute name="data-value">
<xsl:number count="chapter"/>
<xsl:text>.</xsl:text>
<xsl:number count="section"/>
<xsl:text>.</xsl:text>
<xsl:number count="subsection"/>
<xsl:text>.</xsl:text>
</xsl:attribute>
<a href="#{@id}">
<xsl:value-of select="title"/>
</a>
<xsl:if test="subsubsection">
<ul>
<xsl:apply-templates mode="indice"/>
</ul>
</xsl:if>
</li>
</xsl:template>
<xsl:template match="subsubsection" mode="indice">
<li>
<xsl:attribute name="data-value">
<xsl:number count="chapter"/>
<xsl:text>.</xsl:text>
<xsl:number count="section"/>
<xsl:text>.</xsl:text>
<xsl:number count="subsection"/>
<xsl:text>.</xsl:text>
<xsl:number count="subsubsection"/>
<xsl:text>.</xsl:text>
</xsl:attribute>
<a href="#{@id}">
<xsl:value-of select="title"/>
</a>
</li>
</xsl:template>
</xsl:stylesheet>
(and probably refactor the generation of the attribute data-value
) and then use some CSS like this:
li:before {
content:attr(data-value);
}
And if your @id
always already contains the correct number you could just replace (translate
) the s
in @id
with "nothing" and show that using the CSS pseudo element...
来源:https://stackoverflow.com/questions/27921026/table-of-contents-xsl