Text value templates
TIL: Be careful with text value templates in mixed content.
In XSLT, text value templates can save a few keystrokes and, in some contexts, make your code a lot clearer. My XSLT coding habits predate them and I don’t tend to use them very often.
If you do use them, it’s worth remember how they interact with whitespace stripping. What happened was, I was looking at some HTML and I thought the text decoration of a link was a bit weird. When I looked at the source, I was very surprised by the whitespace.
Here’s a simple example to illustrate what happened. Consider some XML:
<p>
<pubdate>2023-01-10</pubdate>
</p>
Suppose you want to transform that pubdate
into something more human
readable. You decide to use fn:format-date
:
<xsl:template match="pubdate">
<time datetime="{.}">
<xsl:sequence select="format-date(xs:date(.), '[D01] [MNn,*-3] [Y0001]')"/>
</time>
</xsl:template>
All well and good, that transformation produces:
<p>
<time datetime="2023-01-10">10 Jan 2023</time>
</p>
Suppose on another day, you decide to do it with a text value template to save yourself a few keystrokes:
<xsl:template match="pubdate" expand-text="yes">
<time datetime="{.}">
{ format-date(xs:date(.), '[D01] [MNn,*-3] [Y0001]') }
</time>
</xsl:template>
Same thing, right? Not quite. The output now looks like this:
<p>
<time datetime="2023-01-10">
10 Jan 2023
</time>
</p>
Because the content of the time
element contained non-whitespace
characters, whitespace stripping didn’t apply. That means the
whitespace I added to make the code easier to read wound up in the
output. This probably doesn’t matter much, especially in HTML,
except that if the extra whitespace is inside a link, the text
decoration (like underlining) that applies to the link also applies
to the extra whitespace!
You could mitigate this by writing
<xsl:template match="pubdate" expand-text="yes">
<time datetime="{.}">{
format-date(xs:date(.), '[D01] [MNn,*-3] [Y0001]')
}</time>
</xsl:template>
or even
<xsl:template match="pubdate" expand-text="yes">
<time datetime="{.}"
>{ format-date(xs:date(.), '[D01] [MNn,*-3] [Y0001]') }</time>
</xsl:template>
But those are just awful.
Maybe this is why I don’t use text value templates very much.