How to Detect Clamped Text in React

When working with text-heavy web applications, you might come across scenarios where long text needs to be truncated after a certain number of lines. CSS properties like -webkit-line-clamp make this easy by limiting the visible lines of text, but you might need to know when that text has been clamped for purposes like showing a “Read More” button or providing feedback to users. In this post, I’ll walk you through how to detect clamped text in a React component using JavaScript.

Thank me by sharing on Twitter 🙏

In this guide, I’ll explain what clamping is, how to detect clamped text in React, and how to implement this solution in your project.

Understanding Text Clamping

Clamping is the process of visually truncating content after a specific number of lines. For example, with -webkit-line-clamp, you can restrict text to two or three lines, adding an ellipsis if the text is too long. This method is commonly used in scenarios like blog previews, product descriptions, or article summaries, where you want to maintain a clean layout by showing a limited amount of text.

Here’s an example of clamping text with CSS:

CSS
.clamped-text {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

In this CSS, the -webkit-line-clamp property limits the number of visible lines to three. However, this truncation happens purely on the front end, and CSS doesn’t provide a way to check if the text has actually been truncated. That’s where JavaScript and React come into play.

The Challenge: Detecting Clamped Text in React

When working with React, you may want to know if a piece of text is clamped to display additional controls, like a “Show More” button, or to notify users that some of the content is hidden. Unfortunately, CSS alone doesn’t offer a way to detect clamped text. The good news is that JavaScript can do the job.

The basic idea is to compare the height of the element containing the text with the height of the fully expanded text. If the full height exceeds the visible height (i.e., the scrollHeight is greater than the clientHeight), the text is clamped.

Step 1: Setting Up the React Component

First, let’s create a simple React component to display some clamped text. We’ll use the useRef hook to gain direct access to the DOM element, and useEffect to check if the text is clamped when the component renders.

Here’s what the basic component looks like:

TypeScript
import React, { useEffect, useRef, useState } from 'react';

const ClampedText = ({ text }: { text: string }) => {
  const textRef = useRef<HTMLDivElement>(null);
  const [isClamped, setIsClamped] = useState(false);

  useEffect(() => {
    const checkClamping = () => {
      const element = textRef.current;
      if (element) {
        const currentStyle = window.getComputedStyle(element);
        const lineClamp = parseInt(currentStyle.getPropertyValue('-webkit-line-clamp'), 10);

        if (lineClamp > 0 && currentStyle.getPropertyValue('overflow') === 'hidden') {
          setIsClamped(element.scrollHeight > element.clientHeight);
        } else {
          setIsClamped(false);
        }
      }
    };

    checkClamping();

    window.addEventListener('resize', checkClamping); // Handle resizing
    return () => window.removeEventListener('resize', checkClamping);
  }, [text]);

  return (
    <div>
      <div
        ref={textRef}
        style={{
          display: '-webkit-box',
          WebkitLineClamp: 3,
          WebkitBoxOrient: 'vertical',
          overflow: 'hidden',
          textOverflow: 'ellipsis',
        }}
      >
        {text}
      </div>
      {isClamped && <p>Text is clamped</p>}
    </div>
  );
};

export default ClampedText;

Step 2: Breaking Down the Logic

Let’s walk through the key parts of this code:

Using useRef to Access the DOM

In React, we can’t directly access DOM elements like we can with plain JavaScript. Instead, we use the useRef hook to create a reference to the DOM element. The textRef variable in this example holds a reference to the div that contains the text we want to clamp.

TypeScript
const textRef = useRef<HTMLDivElement>(null);

This allows us to interact with the underlying DOM element directly, which is crucial for checking its height later.

Checking Clamping with scrollHeight and clientHeight

Next, in the checkClamping function, we compare the scrollHeight of the element to its clientHeight. The scrollHeight is the total height of the content inside the element, including any overflow. The clientHeight is the visible height of the element. If the scrollHeight exceeds the clientHeight, it means the content is clamped.

TypeScript
if (element.scrollHeight > element.clientHeight) {
  setIsClamped(true);
}

We also check if the -webkit-line-clamp property is set to a positive value and if the overflow is set to hidden, as these are essential for clamping to occur.

Responding to Window Resize

Text clamping might behave differently depending on the size of the window. For example, reducing the width of the window may cause more clamping, while increasing the width may reduce it. To handle this, we add a resize event listener that re-checks whether the text is clamped when the window is resized.

TypeScript
window.addEventListener('resize', checkClamping);

This ensures that we can accurately detect when clamping changes based on the window size.

Step 3: Adding a User-Friendly Indicator

In this example, we render a simple <p> element that informs the user when the text is clamped:

TypeScript
{isClamped && <p>Text is clamped</p>}

This conditional rendering ensures that the message only appears if the text is indeed clamped. You can easily modify this to display a “Read More” button or any other UI element you prefer.

Step 4: Testing the Component

With the component complete, you can test it by passing different lengths of text to see how it behaves. You’ll notice that the message changes based on whether the text fits within the specified number of lines or not.

For example:

TypeScript
<ClampedText text="This is a long text that might be clamped depending on the width of the container and the number of lines allowed." />

You can also modify the WebkitLineClamp value in the component’s style to adjust the number of lines before clamping occurs.

Conclusion

Detecting clamped text in a React application can be incredibly useful for improving user experience, especially when dealing with dynamic content. By using the scrollHeight and clientHeight properties, we can easily determine whether a piece of text is being truncated and take appropriate action, such as displaying a “Read More” button or informing the user that some content is hidden.

The approach outlined in this post provides a simple and effective way to handle clamped text in your React components. Whether you’re working with long blog posts, product descriptions, or any other type of text, this solution gives you full control over how clamping is detected and managed.

Share this:

Leave a Reply