I'm fairly new to XSL, XPATH etc. Some of this code is mine, some are someone else's.
Problem: When the template below gets called with the templates I've outlined further below, all the xsl:text
nodes after the if test is output as a string instead of an HTML node, and thus the icon is not rendered.
This question has to do with understanding the why? of what's going on. My exact question is at the bottom of this post.
So, I have a template that I call that generates SVG elements with a <use>
element for use with an SVG sprite.
<xsl:template name="svg-link">
<xsl:param name="svg-id"/>
<xsl:param name="svg-class"/>
<xsl:param name="svg-title"/>
<span class="{$svg-class} svgstore svgstore--{$svg-loc}">
<svg>
<xsl:if test="$svg-title != ''">
<title><xsl:value-of select="$svg-title"/></title>
</xsl:if>
<xsl:text disable-output-escaping="yes"><use xlink:href="</xsl:text>
<xsl:value-of select="concat('#', $svg-loc)" />
<xsl:text disable-output-escaping="yes">"></use></xsl:text>
</svg>
</span>
</xsl:template>
All sorts of templates call/apply this template. There is one in particular that I'm having an issue with. We have two snippets implemented by the CMS that output the same markup, but the configurations for the snippets are implemented differently, i.e. Page Template A vs Page Template B. The snippet in question is made of multiple XSL templates. The templates are organized like so:
Here's some simplified pseudo-code demonstrating above:
<xsl:template name="snippet">
<xsl:param name="outer-classes"/>
<xsl:param name="inner-classes"/>
<xsl:variable name="items">
<xsl:call-template name="snippet-model"/>
</xsl:variable>
<!-- Render Snippet if it has content. -->
<xsl:if test="count( $items )">
<div class="{ $outer-classes }">
<div class="{ $inner-classes }">
<xsl:copy-of select="$items">
</div>
</div>
</xsl:if>
</xsl:template>
<!-- Placeholder. Defined by each page template. -->
<xsl:template name="snippet-model"/>
<xsl:template name="snippet-item">
<xsl:param name="a"/>
<xsl:param name="b"/>
<xsl:param name="b"/>
<div class="snippet-item {$a}">
<xsl:apply-templates select="$b"/>
<xsl:call-template name="svg-link">
<xsl:with-param name="svg-id">alpha</xsl:with-param>
<xsl:with-param name="svg-class">alpha</xsl:with-param>
<xsl:with-param name="svg-title">The Title</xsl:with-param>
</xsl:call-template>
</div>
</xsl:template>
And an example of how a page template uses the above:
<xsl:template match="table[@class = 'snippet-alpha']">
<xsl:call-template="snippet">
<xsl:with-param name="outer-classes">page-template-a other</xsl:with-param>
<xsl:with-param name="inner-classes">some-template-modifier</xsl:with-param>
</xsl:call-template>
</xsl:template>
<!-- Template definition of `snippet-model` template. -->
<xsl:template name="snippet-model">
<!-- Another page template might not use `tbody/tr` to loop over. -->
<xsl:for-each select="tbody/tr">
<xsl:call-template="snippet-item">
<xsl:with-param name="a" select="td[1]"/>
<xsl:with-param name="b" select="td[2]"/>
<xsl:with-param name="c" select="td[3]"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
I've narrowed down my issue to likely be the xsl:variable
capturing the results of xsl:call-template
in the snippet
template. And/Or the referencing of that variable later with xsl:copy-of
.
Below I have working and non-working solutions, all of which I do not fully grokk why they may or may not work.
xmlns:xlink="http://www.w3.org/1999/xlink"
to xsl:stylesheet
for the file that contains the svg-link
template and then modding svg-link
template, see code below this list. xsl:variable
that captures the results of xsl:call-template
with xsl:copy-of
. I replace xsl:copy-of
with a second xsl:call-template
that is identical to that of the call that was done inside the variable.xsl:sequence
instead of xsl:copy-of
.xsl:variable
that captures the results of xsl:call-template
with the as
attribute. I.e. as="node()*"
.<xsl:template name="svg-link">
<xsl:param name="svg-id"/>
<xsl:param name="svg-class"/>
<xsl:param name="svg-title"/>
<span class="{$svg-class} svgstore svgstore--{$svg-loc}">
<svg>
<xsl:if test="$svg-title != ''">
<title><xsl:value-of select="$svg-title"/></title>
</xsl:if>
<use xlink:href="{concat( '#', $svg-loc )}"></use>
</svg>
</span>
</xsl:template>
Question: Why are some of the contents of the svg-link
template being output as a string (instead of HTML) based on how the result of a call to xsl:call-template
is captured/called? As you can see, I have working and non-working solutions - I would like to know why. Thanks!
First of all, disable-output-escaping
is an optional serialization feature. Additionally, the XSLT 2 or 3 specs spell out when it doesn't work at all, see https://www.w3.org/TR/xslt-30/#disable-output-escaping
If output escaping is disabled for an
xsl:value-of
orxsl:text
instruction evaluated when temporary output state is in effect, the request to disable output escaping is ignored.
and https://www.w3.org/TR/xslt-30/#dt-temporary-output-state
xsl:variable, xsl:param, xsl:with-param, xsl:function, xsl:key, xsl:sort, xsl:accumulator-rule, and xsl:merge-key always evaluate the instructions in their contained sequence constructor in temporary output state
So inside your xsl:variable
any disable-output-escaping
can't work.
The whole attempt to use it to try to construct an SVG use
element is completely unnecessary, you can create any result elements as literal result elements e.g. <use xlink:href="{concat( '#', $svg-loc )}"></use>
(with an appropriate XLink namespace declaration in scope for the attribute from that namespace), or, if you need to compute part of the name or namespace, using xsl:element
.