10

I am using the Line-Clamp property (with a backup max-height) to limit the number of lines to show in a React component. I would like to have an optional link afterwards that will expand this content to its full length, but only if the current number of lines is greater than the line-clamp number.

The number of lines is fixed (3) so I guess I could just calculate the current height of the div and then compare it to the expected height of 3 lines at standard text size?

But then if someone decides to put different non text content in it it might not work as intended. Is there a specific way to get the number of lines of text in a container?

 const {useState} = React; const ClampedDiv = ({ children, showLinkProp }) => { const [open, setOpen] = useState(false); // This is where I'd do the line number calculation, but it's just // using a placeholder instead. let showLink = false; if (showLinkProp) { showLink = true; } let textClass = "text"; if (open) { textClass += " open"; } return <div className="container"> <span class={textClass}>{children}</span> {showLink && !open && ( <button onClick={() => setOpen(true)}>Open</button> )} </div> }; const Component = () => ( <React.Fragment> <ClampedDiv> Some content that should not show a read more </ClampedDiv> <ClampedDiv showLinkProp> Some content that should show a read more. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys 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 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. </ClampedDiv> </React.Fragment> ); ReactDOM.render( <Component />, document.body )
.text { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; max-height: calc(3 * 1.5 * 14px); font-size: 14px; line-height: 1.5; } .open { -webkit-line-clamp: unset; max-height: none; } .container { background-color: crimson; color: white; margin-bottom: 15px; padding: 15px; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script> <div id="react"></div>

5
  • may be get the inenerHtml anf then use string.split(" "), which returns the number of texts? Commented Mar 23, 2021 at 13:33
  • 1
    Did you check stackoverflow.com/q/783899/2873538 and this? Commented Mar 24, 2021 at 14:46
  • 1
    I've seen them but the first is just base JS - I adapted the solution to React, I'll post my version if I get some time later. Commented Mar 24, 2021 at 16:40
  • The gist of the solution is a div that can expand to any size inside a max-height container. You then measure the height of that box compared to the container and the expected line-height. If it's bigger you show the button. The issue is that using line-clamp automatically limits the size of the box, so you can't use it and have to use max-height on the container instead, which is sad as you don't get the automatic ellipsis. But it does work! Commented Mar 24, 2021 at 16:43
  • 1
    @JamesPaterson did ever get a solution to this? Commented Jul 18, 2022 at 8:35

1 Answer 1

3

If you just want to check for hidden content inside your element, you can use the useRef hook to refer to the specific element.

And then, use scrollHeight to get the height of the content of the element.

The Element.scrollHeight read-only property is a measurement of the height of an element's content, including content not visible on the screen due to overflow.

And compare it to the clientHeight to see if there is hidden content.

 const { useState, useRef, useLayoutEffect } = React; const ClampedDiv = ({ children }) => { const [open, setOpen] = useState(false); const ref = useRef(null); // This is where I'd do the line number calculation, but it's just // using a placeholder instead. const [showLink, setShowLink] = useState(false); useLayoutEffect(() => { if (ref.current && ref.current.clientHeight < ref.current.scrollHeight) { setShowLink(true) } }, [ref]) let textClass = "text"; if (open) { textClass += " open"; } return <div className="container"> <span class={textClass} ref={ref}>{children}</span> {showLink && !open && ( <button onClick={() => setOpen(true)}>Open</button> )} </div> }; const Component = () => ( <React.Fragment> <ClampedDiv> Some content that should not show a read more </ClampedDiv> <ClampedDiv> Some content that should show a read more. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys 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 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 penter code hereublishing software like Aldus PageMaker including versions of Lorem Ipsum. </ClampedDiv> </React.Fragment> ); ReactDOM.render( <Component />, document.body )
.text { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; max-height: calc(3 * 1.5 * 14px); font-size: 14px; line-height: 1.5; } .open { -webkit-line-clamp: unset; max-height: none; } .container { background-color: crimson; color: white; margin-bottom: 15px; padding: 15px; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script> <div id="react"></div>

As you can see, now the implementation of the check to show more content is specific to the child component and depends on the content passed by the parent component.

Sign up to request clarification or add additional context in comments.

1 Comment

The problem here is that you can't have a button to collapse -- if you check the viability of that button based on the height comparsion, you can either choose to have a collapse button both when the text is expanded and when the original text needs no truncation.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.