How do you truncate html text in React?

Viewed 1406

I am trying to truncate the following html to 5 characters:

<b>Hello</b> how are you today?

The result I'm looking for is:

Hello

However, how do I ignore the html tags in the truncation so that it doesn't result in the following?

<b>He

I am using an html parser, so I can't truncate the string after using that. Here is my code which, needless to say, doesn't work!

import React from 'react';
import parse from 'html-react-parser';
import Typography from '@material-ui/core/Typography';


const Message= () => {
  const message= "<b>Hello</b> how are you today?"
  const messageParsed = parse(message);

  return (
          <Typography variant="body2">
            {messageParsed.substr(0, 5)}
          </Typography>
  );
};

export default Message;

What is the correct approach?

Many thanks,

Katie

3 Answers

You can keep the html structure by using Ranges.

To get the html with a certain length, call htmlToLength(html, length) below.

You can also get a DocumentFragment by calling htmlToNodeWithLength(html, length).

The heart of this algorithm is findRangeWithLength(range, length), which shrinks the range from the end (recursively), until it has the given length.

function htmlToLength(html, length) {
  const trimmedNode = htmlToNodeWithLength(html, length);

  const container = document.createElement("div");
  container.appendChild(trimmedNode);
  return container.innerHTML;
}

function htmlToNodeWithLength(html, length) {
  // Only for measurement. Never added to DOM.
  const container = document.createElement("div");
  container.innerHTML = html;

  const fullRange = document.createRange();
  fullRange.setStart(container, 0);
  fullRange.setEnd(container, 1);

  const range = findRangeWithLength(fullRange, length);
  return range.cloneContents();
}

function findRangeWithLength(range, length) {
  if (rangeLength(range) < length) return range;

  // Find the childNode with at least length content.
  for (const childNode of range.endContainer.childNodes) {
    range.setEnd(childNode, lastEndOffset(childNode));
    if (rangeLength(range) >= length) {
      return findRangeWithLength(range, length);
    }
  }

  // There are no child nodes long enough. It's a text node.
  const diff = length - rangeLength(range) + range.endOffset;
  range.setEnd(range.endContainer, diff);
  return range;
}

function lastEndOffset(node) {
  return node.childNodes.length || node.textContent.length;
}

function rangeLength(range) {
  return range.toString().length;
}

const html = "<p>No <span></span><b>Hello</b> <i>World</i></p>";
const length = 7;
const trimmedNode = htmlToNodeWithLength(html, length);

document.querySelector(".raw-input").textContent = html;
document.querySelector(".trimmed").appendChild(trimmedNode);
document.querySelector(".length").textContent = length;
document.querySelector(".input").innerHTML = html;
document.querySelector(".raw-output").textContent = htmlToLength(html, length);
<h2>Raw HTML input</h2>
<div class="raw-input"></div>
<h2>Rendered Input</h2>
<div class="input"></div>
<h2>Rendered output to length <span class="length">?</span></h2>
<div class="trimmed"></div>
<h2>Raw HTML output</h2>
<div class="raw-output"></div>

what I understand is that you have an HTML text and the following operations are required

  1. Remove the HTML tags
  2. Truncate the string after step 1 to 5 characters.

In that case, it can be done using a regular expression.

import React from "react";
import "./styles.css";

export default function App() {
  const message = "<b>Hello</b> how are you today?";
  const messageString = message.replace(/<(.|\n)*?>/g, '');

  const subText = messageString.substring(0, 5);

  return <div>{subText}</div>;
}

Check here for a working sample.

Check out this & this link to know more about how to remove HTML tags from string in JS.

About the package html-react-parser this does not return the string hence the substring method will not work. It returns an array. In your case the line parse(<b>Hello</b> how are you today?); will return the following enter image description here

One option is that you could use _truncate from lodash/truncate like so:

import _truncate from "lodash/truncate";
import ReactHtmlParser from "react-html-parser";

function ParsedText() {
  var text = "<b>Hello</b> how are you today?";


  return (
    <div> 
      {ReactHtmlParser(_truncate(text, { length: 5 }))}
    </div>
  )
Related