How can I check whether line-clamp is enabled?

I have a dynamic text in a span. I would like to use line-clamp: 2.

In that case there are max. 2 lines of text and the rest is truncated by .

That works with:

display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;

My problem: If the content is truncated, a tooltip should be displayed. How can I detect whether the text is clamped?

The height of the element is the same, innerHTML is same… I have no further idea…

  • Can’t you use just JavaScript to do the whole work?

    – 

  • Linked to stackoverflow.com/q/7738117/104380

    – 

Detecting CSS line-clamp by javascript can be made by comparing the scrollHeight and the clientHeight of the “clamped” element.

The “true” height of the element is clipped by the overflow: hidden CSS property, but the DOM property scrollHeight will report the full height, while the clientHeight reports the rendered height.

enter image description here

The below example shows a clamped text.
Try hovering it to see if detection logged. (text is editable)

const isTextClamped = elm => elm.scrollHeight > elm.clientHeight

new ResizeObserver(e => {
  console.clear()
  console.log( isTextClamped(e[0].target) )
}).observe(elem);
p {
  width: 200px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  resize: both; /* allowing resize for this demo only */
}
<p contenteditable id='elem' spellcheck="false">
  <strong>Resize me - </strong>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
</p>

👉 Here’s a Codepen which illustrates this live

You can check for the elements scrollHeight exceeding the clientHeight:

function multiLineOverflows() {
    const el = this._element.nativeElement;
    return el.scrollHeight > el.clientHeight;
}

Here is a solution for NextJS, React, Typescript and Tailwind, including a button for “Read More…” that appears when text is clamped.

A description before the code:

This is a react component that gets post as prop (could be anything else). The post contains content that we should display, with clamp of 4 lines until user clicks on “Read More…”.

The content ref (useRef hook) holds a reference to the content div in which content should be displayed.
First useState hook holds the state isClamped: true if content is clamped and false otherwise.
Second useState holds the state isExpanded: true if user has clicked on “Read More…” and false otherwise.
Then, useEffect hook, called only on mount because of the empty array set to it, adds event listener to window resize (that may affect the numbers of lines of the content). When the window is being resized, contentRef (div with content) scrollHeight is compared to clientHeight, exactly like in the other answers above. If scrollHeight is bigger, isClamped will be set to true, and false otherwise.

Now, in the div that contains the content, if isExpanded is false (user hasn’t clicked on “Read More…”) then className will be set to “line-clamp-4” (which will limit content to 4 lines), otherwise className will be set to “line-clamp-none” (no clamp).

Lastly, in the div that contains the “Read More…” will be shown if isClamped is true (so text is clamped).

import {useState, useRef, useEffect}

interface PostItemProps {
      post: Post
}

export default function PostItem({post}: PostItemProps){
    
  const contentRef = useRef<HTMLDivElement>(null)
  const [isClamped, setClamped] = useState(false)
  const [isExpanded, setExpanded] = useState(false)

  useEffect(() => {
    // Function that should be called on window resize
    function handleResize() {
      if (contentRef && contentRef.current) {
        setClamped(
          contentRef.current.scrollHeight > contentRef.current.clientHeight
        )
      }
    }

    // Add event listener to window resize
    window.addEventListener('resize', handleResize)

    // Remove event listener on cleanup
    return () => window.removeEventListener('resize', handleResize)
  }, []) // Empty array ensures that it would only run on mount

return (
    <div>
     <div ref={contentRef} className={`${ !exapnded ? 'line-clamp-4' : 'line-clamp-none' }> {post.content} </div>
     </div>
       {isClamped && (
          <div className="flex justify-end">
            <button
              className="font-bold text-title"
              onClick={() => setExpanded(true)}
            >
              See More...
            </button>
          </div>
        )}
    </div>
 )
}

A simple solution based on getClientRects().length that works for me.

$('.text-inline').each(function() {
  var textLineCount = $(this)[0].getClientRects().length;
  var lineClampValue = $(this).parent().css('-webkit-line-clamp');
  if (textLineCount > lineClampValue) {
    $(this).parent().addClass('cut');
  }
});
.text-block {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.text-block.expand {
  -webkit-line-clamp: initial;
}

.show-text {
  display: none;
}

.cut+.show-text {
  display: initial;
}

.expand+.show-text {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script>
<div class="text-block">
  <span class="text-inline">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore   magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum</span>
</div>
<button class="show-text" onclick="$(this).prev().addClass('expand')">Read more</button>

Leave a Comment