Reputation: 2915
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>
Upvotes: 10
Views: 8536
Reputation: 91
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.
Upvotes: 3