10

I need to cut rich text field value to render only 100 first symbols and '...' at the end. The issue is rich text field value looks like

<strong style="background-color: #ffffff; margin: 0px; padding: 0px; text-align: justify;">Lorem Ipsum</strong><span style="background-color: #ffffff; text-align: justify;">&nbsp;is<em><strong> simply dummy text of the printin</strong></em>g and typesettin<a href="http://https://sitecore.stackexchange.com/">g indust</a>ry. Lorem Ipsu<em><strong>m has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It </strong></em>has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum</span>

So if I use a simple substring function I will have

<strong style="background-color: #ffffff; margin: 0px; padding: 0px; text-align: justify;">Lorem I

=> Lorem I

instead of

<strong style="background-color: #ffffff; margin: 0px; padding: 0px; text-align: justify;">Lorem Ipsum</strong><span style="background-color: #ffffff; text-align: justify;">&nbsp;is<em><strong> simply dummy text of the printin</strong></em>g and typesettin<a href="http://https://sitecore.stackexchange.com/">g indust</a>ry. Lorem Ipsu<em><strong>m has been the

=>

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the

Probably sitecore libraries has a functionality to complete it?

1
  • 3
    But what if rich text contains large table and image? Result of such cut is unpredictable. Usually if you need to display short version of rich text it is better to have additional simple text field (single/multi line) with well prepared content
    – Daniil
    Nov 29, 2016 at 9:16

5 Answers 5

10

This problem is best addressed with CSS, where you are trying to control the size of a rendered string, because 100 characters measures a different length depending on what characters they contain. Trite example: 100 x i is a very different size to 100 x W.

In the event you're actually doing a "more" break, the best thing is to get content authors to place an HTML comment into the text to identify the appropriate place to break. I believe this technique is used widely in blogging systems like WordPress. A simple example is <!-- #More -->.

Truncating the text is fraught with danger. Using the HtmlAgilityPack for this is only going to double your pain.

Edit: use the RadEditor snippet functionality to make it easier for authors!

2
7

Yes, there is. Go with HtmlAgilityPack.dll. It's already there with your Sitecore site.

See here for details: HtmlAgilityPack substring of all by length


EDIT

Mark commented that this is a link only answer, so I'll copy the code from the linked SO question. Please be aware that it's not my code and all the credits go to Serge Belov

public string TrimRichText(string input, int maxLength)
{
    var doc = new HtmlDocument();
    doc.LoadHtml(input);

    // Get text nodes with the appropriate running total
    var acc = 0;
    var nodes = doc.DocumentNode
        .Descendants()
        .Where(n => n.NodeType == HtmlNodeType.Text && n.InnerText.Trim().Length > 0)
        .Select(n =>
        {
            var length = n.InnerText.Trim().Length;
            acc += length;
            return new { Node = n, TotalLength = acc, NodeLength = length };
        })
        .TakeWhile(n => (n.TotalLength - n.NodeLength) < maxLength)
        .ToList();

    // Select element nodes we intend to keep
    var nodesToKeep = nodes
        .SelectMany(n => n.Node.AncestorsAndSelf()
            .Where(m => m.NodeType == HtmlNodeType.Element));

    // Select and remove element nodes we don't need
    var nodesToDrop = doc.DocumentNode
        .Descendants()
        .Where(m => m.NodeType == HtmlNodeType.Element)
        .Except(nodesToKeep)
        .ToList();

    foreach (var r in nodesToDrop)
        r.Remove();

    // Shorten the last node as required
    var lastNode = nodes.Last();
    var lastNodeText = lastNode.Node;
    var text = lastNodeText.InnerText.Trim().Substring(0,
            lastNode.NodeLength - lastNode.TotalLength + maxLength);
    lastNodeText
        .ParentNode
        .ReplaceChild(HtmlNode.CreateNode(text), lastNodeText);

    return doc.DocumentNode.OuterHtml;
}
2
  • Link-only answer.
    – Mark Cassidy
    Nov 30, 2016 at 4:19
  • Answer updated. Code copied from the linked SO question.
    – Marek Musielak
    Nov 30, 2016 at 8:32
4

HtmlAgilityPack.dll is used.

public class Renderer
      {
        public string DoRender(HtmlDocument document, int length)
        {
          int totalLength = 0;
          int i = 0;
          foreach (var htmlNode in document.DocumentNode.Descendants())
          {
            i++;
            if (htmlNode.NodeType != HtmlNodeType.Text)
            {
              continue;
            }
            totalLength += htmlNode.InnerText.Length;
            if (totalLength >= length)
            {
              int diff = totalLength - length;
              htmlNode.InnerHtml = htmlNode.InnerText.Substring(0, htmlNode.InnerText.Length - diff);
              //remove redundant elements
              var htmlNodes = document.DocumentNode.Descendants().Skip(i).ToList();
              foreach (var node in htmlNodes)
              {
                node.Remove();
              }
              break;
            }
          }
          return document.DocumentNode.OuterHtml;
        }
      }

Test:

public class RendererTests
  {
    [Theory()]
    [InlineData(22, "<p>Lorem ipsum <span>werwerc <i>sdf</i> sdf</span> <b>Lorem</b></p>", "<p>Lorem ipsum <span>werwerc <i>sd</i></span></p>")]
    [InlineData(30, "<p>Lorem ipsum <span>werwerc <i>sdf</i> sdf</span> <b>Lorem</b></p>", "<p>Lorem ipsum <span>werwerc <i>sdf</i> sdf</span> <b>Lo</b></p>")]
    public void DoRenderTest(int length, string input, string output)
    {
      var renderer = new Renderer();
      var doc = new HtmlDocument();
      doc.LoadHtml(input);

      var doRender = renderer.DoRender(doc, length);
      Assert.Equal(output, doRender);
    }
  }
1

Against all provided solutions there is one more: usage of jQuery.dotdotdot plugin. It is easy in use and configurable enough to fit your needs.

$(document).ready(function() {
    $("#wrapper").dotdotdot({
        /*  The text to add as ellipsis. */
        ellipsis    : '... ',
        /*  How to cut off the text/html: 'word'/'letter'/'children' */
        wrap        : 'word',
        /*  Wrap-option fallback to 'letter' for long words */
        fallbackToLetter: true,
        /*  jQuery-selector for the element to keep and put after the ellipsis. */
        after       : null,
        /*  Whether to update the ellipsis: true/'window' */
        watch       : false,
        /*  Optionally set a max-height, can be a number or function.
            If null, the height will be measured. */
        height      : null,
        /*  Deviation for the height-option. */
        tolerance   : 0,
        /*  Callback function that is fired after the ellipsis is added,
            receives two parameters: isTruncated(boolean), orgContent(string). */
        callback    : function( isTruncated, orgContent ) {},
        lastCharacter   : {
            /*  Remove these characters from the end of the truncated text. */
            remove      : [ ' ', ',', ';', '.', '!', '?' ],
            /*  Don't add an ellipsis if this array contains 
                the last character of the truncated text. */
            noEllipsis  : []
        }
    });
});

P.S. don't forget to wrap this JS code to be executed only in normal mode.

0

No Sitecore doesn't have anything like this out of the box. I usually try to avoid things like this because truncating HTML output (what the RTE holds) can (visually) break your page. If you need to do this on overview pages or teaser blocks then it's better to have an additional plain-text field, e.g. multi-line text.

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.