Maintained by: David J. Birnbaum (djbpitt@gmail.com) Last modified: 2022-11-02T13:34:32+0000
Attribute value templates (AVTs) are a strategy for inserting the value of XPath expressions into the attribute value of created content. Two typical situations where AVTs are useful are:
You are creating a table of contents (TOC) and you want to create links (using
the HTML <a href="#fragment_identifier">
notation) in the TOC that will point to the various sections in the body of the
document. Those links might make use of values taken from text in the input XML;
for example, if each chapter has a unique one-word identifier in an attribute
value, like
<chapter xml:id="introduction">
, you
might want your links to read something like
<a href="#introduction">
. You can use an
AVT to insert the value of the @xml:id
attribute from the input XML into the value of the
@href
attribute of the output
HTML.
You are creating a research paper with footnotes. In your input XML, you’ve written the footnotes in the body of the text, to keep them where they belong logically, e.g.,
<paragraph>Here is a sentence. Here is another sentence.<fn>Here
is a footnote, the number for which will go here.</fn> And here is
one more sentence, after the footnote number.</paragraph>
You want this to be rendered like the following, with the footnote number inserted automatically and the footnote text rendered at the bottom of the page:
Here is a sentence. Here is another sentence.1 And here is one more sentence, after the footnote number.
The footnote number should be generated automatically, so that when you add or
delete footnotes from the XML, the XSLT will still generate correct, consecutive
numbers in the output HTML. Furthermore, you want the footnote numbers to be
links, so that the user can click on them to jump to the footnote text (and
click on the footnote text to jump back to the previous location, although the
back button would work in this case, as well). In this situation you can
generate the numbers automatically by using XPath to figure out which footnote
is which, and you can use an AVT to incorporate those numbers into the values of
the @href
attributes that will take the
user to the notes themselves. For example, you might create HTML like:
<p>Here is a sentence. Here is another sentence.<sup><a href="#note1">1</a></sup>
And here is one more sentence, after the footnote number.</p>
This output uses the HTML <sup>
element
to create a superscript number, and it puts the
<a>
element inside that to make the
number a clickable link. The numerical value itself, inside the
<sup>
element and again at the end of
the value of the @href
attribute, is
generated by the XSLT. For example, the next footnote would have “2” as its
number and “#note2” as the value of its
@href
attribute.
A bit of background: When we create output HTML elements during an XSLT transformation by just typing the raw HTML tags into the stylesheet, we are creating what are called literal result elements (LREs). As the name implies, when you want to have the element markup (the tags) inserted literally into the output result, you just type it in the appropriate place in your stylesheet and it gets created in the output tree. For example, in the following template rule:
<xsl:template match="paragraph">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
the <p>
that is being constructed is an LRE
because it is being specified literally inside the template rule. An LRE may contains
attributes, so if you want to give your paragraph a particular attribute value, you can
create that value literally, as well, using code like the following:
<xsl:template match="paragraph">
<p class="interesting">
<xsl:apply-templates/>
</p>
</xsl:template>
The preceding template rule will take any
<paragraph>
element in the input document,
create a corresponding <p>
element in the
output, associate a @class
attribute with the value
“interesting” with the <p>
element, and process
the children of the original paragraph, inserting the content they generate inside the
newly created <p>
element.
In the immediately preceding example, where we are labeling all paragraphs as
“interesting”, specifying that label as part of the LRE is all we need. In the two
situations described earlier, however, TOC and footnotes, we don’t want every element to
have the same attribute value, so can’t just write the attribute value directly into the
LRE, as we did with the uniform class="interesting"
example. In the TOC, we want each chapter title to point to a different part of the
document with a unique identifier, and in the footnote example we want each footnote to
have a unique number, and the associated link to have a unique value. An AVT lets us
construct these attribute values on the fly, adapting the value to the situation for
each affected node.
An AVT is created by wrapping some XPath inside curly braces within the quotation marks
you are using around an attribute value. For example, assuming chapters in our input XML
document are <chapter>
elements with unique
identifiers in an associated @id
attribute (e.g.,
<chapter id="introduction">
), we can create TOC
links with a template rule like:
<xsl:template match="chapter" mode="toc">
<li>
<a href="#{@id}">
<xsl:apply-templates select="@id"/>
</a>
</li>
</xsl:template>
This will output something like:
<li>
<a href="#introduction">introduction</a>
</li>
The LRE is output literally except for the part of the attribute value that is inside the
curly braces. When using an AVT, content inside curly braces, instead of being output
literally, it is interpreted as an XPath expression (relative to the current context
node, the <chapter>
being processed), and the
value of the expression is inserted into the attribute value. In this case, that means
that the value of the @id
attribute on the
<chapter>
element in the input XML document is
copied into the output HTML as part of the value of the created
@href
attribute. The curly braces are not output
themselves; they serve only to delimit the AVT.
To make the linking work, in the body of the document, where the chapter text itself is
printed, you would have to create a target for the link by using an
@id
attribute. That is, the
@href
attribute specifies where the user will go
upon clicking the link, and the @id
attributes
identify parts of the document as potential targets to which
@href
attributes might point. You can put an
@id
attribute on any element, but
@href
can only go on
<a>
elements. This means that if you want to make
the links bidirectional, you need to use <a>
elements on both ends, and add both @href
and
@id
attributes to those
<a>
elements. In that case:
The entry in the TOC might read:
<li>
<a href="#introduction" id="introduction_toc">introduction</a>
</li>
The chapter title inside the body of the document, above the chapter text, might read:
<h2>
<a href="#introduction_toc" id="introduction">introduction</a>
</h2>
Note, though, that the value in the @href
attribute
begins with a hash mark (#
) and the value in the
@id
attribute doesn’t. The
<a>
inside the
<li>
in the TOC as an
@id
value of
introduction_toc
and the
<a>
inside the
<h2>
in the main body has an
@id
value of
introduction
. Each points to the other by setting
the value of the @href
attribute to the value of
the @id
of the target, preceded by a hash mark
(#
).
If footnotes are encoded as <fn>
elements inside
the source XML, the logical number of each footnote is equal to the number of preceding
footnotes in the document plus one (without the “plus one”, the first footnote would
erroneously be considered number zero, since it has no preceding
<fn>
elements). The following template rule will
create the footnote number in place of the footnote text:
<xsl:template match="fn">
<sup><xsl:value-of select="count(preceding::fn) + 1"/></sup>
</xsl:template>
This uses the XPath count()
function to count the
number of <fn>
elements on the
preceding::
axis (that is, the number that precede
the <fn>
being processed at the moment) and adds
one to that number. To add a link to the footnote itself, which you'll render in a set
of notes at the end of the page, change the template to:
<xsl:template match="fn">
<sup>
<a href="#note{count(preceding::fn) + 1}">
<xsl:value-of select="count(preceding::fn) + 1"/>
</a>
</sup>
</xsl:template>
This sets the value of the @href
attribute as the
concatenation of the string #note
plus the value of
the AVT inside the curly braces
(count(preceding::fn) + 1
). For the first footnote,
this will produce:
<sup><a href="#note1">1</a></sup>
The preceding code inserts the footnote numbers into the main text and makes the links
clickable, but you also need to output the footnotes all together at the end of the page
and put @id
values on them that will let the
clickable links find them. You can do that by using
<xsl:apply templates select="//fn" mode="fn"/>
after you output the main text and then writing a modal
template rule to process footnotes differently from the way they are processed
in the main body of the document. Don’t forget, though: as with the TOC, you’ll need to
create @id
attributes on the targets of the links,
and you can make the links bidirectional. The clickable link has an
@href
that begins with a hash mark
(#
); the target of the link has an
@id
that does not begin with a hash mark.
Attribute value templates are concise and legible, and they are the strategy we use most in our own work. There is an alternative, though, the attribute constructor. Instead of specifying the attribute and its value as part of the LRE, relying on an AVT if part of the value needs to be computed separately each time the template fires, you can specify just the element name as a LRE and then specify the attribute name and value with an attribute constructor. The following template rules are exactly equivalent:
<xsl:template match="chapter" mode="toc">
<li>
<a href="#{@id}">
<xsl:apply-templates select="@id"/>
</a>
</li>
</xsl:template>
<xsl:template match="chapter" mode="toc">
<li>
<a>
<xsl:attribute name="href">
<xsl:text>#</xsl:text>
<xsl:value-of select="@id"/>
</xsl:attribute>
<xsl:apply-templates select="@id"/>
</a>
</li>
</xsl:template>
There is, by the way, also an element constructor
(<xsl:element>
), which can be used as an
alternative to an LRE. See Kay for details. In most cases LREs with calculated
attributes encoded through AVTs will do the job, and there is no need for element or
attribute constructors. The constructors are available, though, as alternatives, and for
certain complex types of calculated content they may provide the only workable strategy.
We’d recommend that you:
Learn to use AVTs in your own work.
Remember that element and attribute constructors
(<xsl:element>
,
<xsl:attribute>
) exist, but don’t worry
about them unless you run into a computed markup situation that looks like it
can’t be resolved any other way.