Digital humanities


Maintained by: David J. Birnbaum (djbpitt@gmail.com) [Creative Commons BY-NC-SA 3.0 Unported License] Last modified: 2022-11-02T13:34:32+0000


Attribute value templates (AVT)

When you might want to use attribute value templates

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:

How AVTs work

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.

Using an AVT to link from a TOC into the body (and back)

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:

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 (#).

Using an AVT to number footnotes and create links

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.

An alternative to attribute value templates

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: