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) &gt; 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 answe
r</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, '&lt;')">
        <xsl:value-of select="substring-before($html, '&lt;')"/>
        <!-- Recurse through HTML -->
        <xsl:call-template name="removeHtmlTags">
          <xsl:with-param name="html" select="substring-after($html, '&gt;')"/>
        </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) &gt; 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

  1. Use substring to strip fields to a certain number of characters - so you don't return everything
  2. For HTML fields, add removeHtmlTags template and use call-template to get the result into an XSL variable first