I am having trouble with some XSLT Filtering. I have changed the Rich text field settings on my schema to include:

<template match="@class[.='cta-button-secondary']">  
    <attribute name="class">cta-button cta-button-secondary</attribute>

within the <stylesheet xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0"> element

I expect this to find any anchor elements with a class attribute containing cta-button-secondary (e.g. <a class="cta-button-secondary" href="#">Link</a>) and replace the class attribute to contain cta-button cta-button-secondary (2 css classes).

After publishing it doesn't work.

I suspect it is my poor knowledge of XLST that is the problem.

I have tried alternate implementations of the same e.g.

<template match="a/@class">
    <if test="contains(.,'cta-button-secondary')">
      <attribute name="class">
        <text>cta-button secondary</text>

...but with no success.

Can anyone help?


<stylesheet xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0">
<output omit-xml-declaration="yes" method="xml" cdata-section-elements="script"></output>
<template match="/ | node() | @*">
        <apply-templates select="node() | @*"></apply-templates>
<template match="*[      (self::br or self::p or self::div)     and      normalize-space(translate(., &apos; &apos;, &apos;&apos;)) = &apos;&apos;     and      not(@*)     and      not(processing-instruction())     and      not(comment())     and      not(*[not(self::br) or @* or * or node()])     and      not(following::node()[not(         (self::text() or self::br or self::p or self::div)        and         normalize-space(translate(., &apos; &apos;, &apos;&apos;)) = &apos;&apos;        and         not(@*)        and         not(processing-instruction())        and         not(comment())        and         not(*[not(self::br) or @* or * or node()])       )])     ]">
    <!-- ignore all paragraphs and line-breaks at the end that have nothing but (non-breaking) spaces and line breaks -->
<template match="br[parent::div and not(preceding-sibling::node()) and not(following-sibling::node())]">
    <!-- Chrome generates <div><br/></div>. Renders differently in different browsers. Replace it with a non-breaking space -->
    <text> </text>
    <template match="@class[.='cta-button-secondary']">  
            <attribute name="class">cta-button cta-button-secondary</attribute>

Component XML:

<Content xmlns="uuid:3f71252b-6e99-47f2-8906-ff4488c188a1">
            <step_title>Expand our impact</step_title>
            <heading_line_1>Expand our impact</heading_line_1>
            <title_emphasis>Line 1</title_emphasis>
            <intro_text>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labasdsadore et dolore magna aliqua.c</intro_text>
            <expand_button_label>More about this</expand_button_label>
                <h3 xmlns="http://www.w3.org/1999/xhtml">How we spent it</h3>
                <ol xmlns="http://www.w3.org/1999/xhtml" class="ordered">
                    <li>ordered list item 1</li>
                    <li>ordered list item 2</li>
                    <li>ordered list item 3</li>
                    <li>ordered list item 4</li>
                    <li>ordered list item 5</li>
                    <li>ordered list item 6</li>
                    <li>ordered list item 7</li>
                    <li>ordered list item 8</li>
                    <li>ordered list item 9</li>
                    <li>ordered list item 10</li>
                    <li>ordered list item 11</li>
                    <li>ordered list item 12</li>
                <p xmlns="http://www.w3.org/1999/xhtml">Thanks to the hard work of our supporters we increased what we spent on cancer services to a record £105.9 million in 2011. That's £10 million more than in 2010.</p>
                <p xmlns="http://www.w3.org/1999/xhtml">For a full breakdown of these charts, take a look at our <a href="#">Annual report and accounts 2011</a> or <a href="#" class="cta-button-secondary">Our 2011 achievements.</a></p></body>
            <quote xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="tcm:5-13343" xlink:title="Quote2"></quote>
            <right_column_image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="tcm:5-13350" xlink:title="Wise with money graph"></right_column_image>
  • It's very difficult to understand your question - Might be worth putting your xslt here.
    – johnwinter
    Commented Apr 25, 2013 at 14:17
  • Question has been updated with the XML and XSLT and this works when using the W3C debugger (w3schools.com/xsl/tryxslt.asp) but still not getting the expected result with SDL Tridion 2011.
    – John Askew
    Commented Apr 25, 2013 at 14:47
  • I now just need to be able to inject a <span> within the <a> elements. See: stackoverflow.com/questions/16275491/… Any help is appreciated...
    – John Askew
    Commented Apr 29, 2013 at 10:39

3 Answers 3


The wording of your question suggests to me that you're mixing concepts... The XSLT filter in a schema is meant to change the content you store in that field not the output of that component. As such, the XSLT filter is applied to content when you save it, not when you publish it.

If you want to change the field content on publish then you should be working on the template, not on the XSLT filter for a schema.

  • Thanks for your help. The existing XLST worked after making an amend to the content and saving. I had assumed that the XLST wouldn't be triggered until publishing.
    – John Askew
    Commented Apr 26, 2013 at 8:19
  • The XSLT filter triggers on save but also as a CMS user navigates within the form view (change tab and may fields). I second the suggestion to template this instead. I'd even suggest a separate set of fields for the CTA (promotional content types always have at least one). However, cta-button isn't too bad of a class name to add in the richtext format area (specific brand or class suggesting style or color would be worse). Commented Jul 2, 2015 at 9:50

When Tridion executes the XSLT of an RTF, it does a couple of things you should be aware of. Firstly, it strips out the XHTML namespace, putting the XML of the RTF into "no namespace", (or if you prefer, the namespace whose URI is an empty string.). It then takes the XML fragment and wraps it in a tag. (I'm a bit vague on this... it's not documented - maybe it's a different element name, but I think it's body). Anyway - suffice it to say that an RTF can potentially have multiple root nodes, so it needs a container.

These things have some consequences:

1) Executing your XSLT in an external editor/debugger won't work unless you make sure your test content is in the "no" namespace. (It won't be the whole Content element either; just the field.) 2) Don't expect a template matching '/' to do exactly what you'd think.

I'm not clear from your question whether either of these is your issue, but both are definitely worth checking.


Add this XSLT match at the bottom of the Filtering XSLT

<template match="a">
        <when test="contains(@class, &apos;cta-button-secondary&apos;)">
                <apply-templates select="node() | @*"></apply-templates>

            <attribute name="class">
                <text>cta-button secondary</text>
            <apply-templates select="node()|@*"></apply-templates>
  • Hi Siva. I tried this XSLT but it didn't work in the W3C debugger and didn't achieve the desired result through SDL Tridion 2011.
    – John Askew
    Commented Apr 25, 2013 at 15:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.