SharePoint - stripping HTML tags in XSL
/Sometimes, when working with XSL (for example, in a content query webpart), you would like to limit the number of characters returned in a field in the template.
Trimming field length in XSL
The itemstyle template may look something like this:
<xsl:template name="FAQ" match="Row[@Style='FAQ']" mode="itemstyle">
<xsl:variable name="SafeLinkUrl">
<xsl:call-template name="OuterTemplate.GetSafeLink">
<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
</xsl:call-template>
</xsl:variable>
<dt>
<a href="{$SafeLinkUrl}" class="title" onclick="GoToLink(this);return false;" target="_self">
<xsl:value-of select="@Title" />
</a>
</dt>
<dd>
<xsl:choose>
<xsl:when test="string-length(@Answer) > 150">
<xsl:value-of select="concat(substring(@Answer,0,150),'...')" disable-output-escaping="yes"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@Answer" disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</dd>
</xsl:template>
The interesting part is where the @Answer field is rendered - if the length of this field is beyond 150 characters, it will return the first 150 characters and append an ellipsis (...)
Problem when we have a HTML field
The technique is pretty simple, but it is not fool proof - when the field you are trying to trim is an HTML / Rich field, you have a big problem - the trimming may suddenly cut off valid HTML, to return an invalid HTML.
Imagine, if the @Answer field was:
<div>
<span>a great answer</span>
</div>
And we perform:
substring(@Answer, 25)
Then we'll get:
<div><span>a great answer</span>
</div>
You are now returning really bad HTML to the browser:
<dd>
<div>
<span>a great answe...
</dd>
See how the HTML isn't terminated correctly with the proper end-tags. If you are lucky, the browser guesses correctly and doesn't break your page. Many times though, it's all down hill from here.
Using XSL to strip HTML tags from your field.
A very simple solution is to add an additional XSL template to strip out the HTML tags from your field before rendering it. A simple version can be found on this article.
1. Add this removeHtmlTags template in your XSL
<xsl:template name="removeHtmlTags">
<xsl:param name="html"/>
<xsl:choose>
<xsl:when test="contains($html, '<')">
<xsl:value-of select="substring-before($html, '<')"/>
<!-- Recurse through HTML -->
<xsl:call-template name="removeHtmlTags">
<xsl:with-param name="html" select="substring-after($html, '>')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$html"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
2. Modify your ItemStyle to use the removeHtmlTags template in an XSL variable
<xsl:template name="FAQ" match="Row[@Style='FAQ']" mode="itemstyle">
<xsl:variable name="SafeLinkUrl">
<xsl:call-template name="OuterTemplate.GetSafeLink">
<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="textAnswer">
<xsl:call-template name="removeHtmlTags">
<xsl:with-param name="html" select="@Answer" />
</xsl:call-template>
</xsl:variable>
<dt>
<a href="{$SafeLinkUrl}" class="title" onclick="GoToLink(this);return false;" target="_self">
<xsl:value-of select="@Title" />
</a>
</dt>
<dd>
<xsl:choose>
<xsl:when test="string-length($textAnswer) > 150">
<xsl:value-of select="concat(substring($textAnswer,0,150),'...')" disable-output-escaping="yes"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$textAnswer" disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</dd>
</xsl:template>
Summary
- Use substring to strip fields to a certain number of characters - so you don't return everything
- For HTML fields, add removeHtmlTags template and use call-template to get the result into an XSL variable first