Expanding content based on height using React

I have successfully implemented the show more/show less feature. However, my issue is that I would like it to be based on the number of lines or screen height rather than the number of characters. This is because the current setup may look awkward on certain screens - being too short on larger screens and too long on smaller ones.

For reference, please take a look at my codesandbox HERE

<DescriptionText>
    {isShowMore ? text.slice(0, 300) : text}
</DescriptionText>
{text && text.length > 300 && (
    <ShowMoreText onClick={toggleReadMore}>
      {isShowMore ? "Show more..." : "Show less"}
    </ShowMoreText>
)}

Answer №1

If you're using styled-components, there's a fantastic utility package that I recommend checking out: polished.js

The specific utility you may be interested in is the ellipsis function.

ellipsis

This CSS feature allows for truncated text with an ellipsis. You have the option to specify a max-width and number of lines before truncation.

ellipsis(width: (string? | number?)?, lines: number): Styles

For example, you can display up to 3 lines when showing more information; otherwise, show the full text.

import { ellipsis } from 'polished';

...

const DescriptionText = styled.div`
  font-size: 14px;
  margin-top: 20px;
  margin-bottom: 20px;
  ${({ showMore }) => showMore && ellipsis(undefined, 3)}
`;

...

const Description = () => {
  const [isShowMore, setIsShowMore] = useState(true);
  const toggleReadMore = () => setIsShowMore(show => !show);

  return (
    <MainContainer>
      <TitleText>Description</TitleText>
      <DescriptionText showMore={isShowMore}>{text}</DescriptionText>
      <ShowMoreText onClick={toggleReadMore}>
        {isShowMore ? "Show more..." : "Show less"}
      </ShowMoreText>
    </MainContainer>
  );
};

https://i.sstatic.net/1cKwn.png https://i.sstatic.net/9UUx2.png

https://codesandbox.io/s/show-more-based-on-height-in-react-5r25s?fontsize=14&hidenavigation=1&module=%2FWrapper.js&theme=dark

In a previous response, you mentioned avoiding new dependencies. The CSS applied through the ellipsis utility could be a solution for you. However, integrating polished into your project is highly recommended due to its array of useful styling utilities.

import styled, { css } from "styled-components";

const DescriptionText = styled.div`
  font-size: 14px;
  margin-top: 20px;
  margin-bottom: 20px;
  ${({ showMore }) =>
    showMore &&
    css`
      display: -webkit-box;
      max-width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: normal;
      word-wrap: normal;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 3;
    `}
`;

To cater to various screen sizes and responsiveness, utilize media queries to specify different initial line displays.

Answer №2

Have you thought about using

window.innerHeight < minHeight
? Ensure that you have defined the value for minHeight

const text = `Lorem ipsum .....`; //
let _shouldTruncate = null;
const minHeight = 750;
const Description = () => {
  const descTextEl = useRef(null);
  const [isShowMore, setIsShowMore] = useState(true);
  const toggleReadMore = () => {
    setIsShowMore(!isShowMore);
  };
  const [txtVal, setTxtVal] = useState(""); //save value to state
  //updates `txtVal` on change of `isShowMore`
  useEffect(() => {
    if (_shouldTruncate === null) {
      _shouldTruncate = window.innerHeight < minHeight;
    }
    setTxtVal(_shouldTruncate && isShowMore ? text.slice(0, 300) : text);
  }, [txtVal, isShowMore]);

  return (
    <MainContainer>
      <TitleText>Description</TitleText>
      <DescriptionText ref={descTextEl}>{txtVal}</DescriptionText>
      {_shouldTruncate && (
        <ShowMoreText onClick={toggleReadMore}>
          {isShowMore ? "Show more..." : "Show less"}
        </ShowMoreText>
      )}
    </MainContainer>
  );
};

UPDATE:

In order to calculate the number of lines, consider implementing the logic provided in this solution:

function countLines() {
   var el = document.getElementById('content');
   var divHeight = el.offsetHeight
   var lineHeight = parseInt(el.style.lineHeight);
   //or use computed lineHeight: 
   //var lineHeight = document.defaultView.getComputedStyle(el, null).getPropertyValue("lineHeight");
   var lines = divHeight / lineHeight;
   alert("Lines: " + lines);
}

Update:

Don't forget to import ReactDom for calculating the style.

Check out this Code Sandbox utilizing window.innerHeight

Explore this Code Sandbox based on the number of lines

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for handling multiple API requests in a React Flux application

I am utilizing an API that provides reports based on the "hourOfDay" parameter. http://localhost:8080/api/v1/reports/61?aggregation=hourOfDay&start=2016-02-10T00:00:00.000Z&stop=2016-02-20T10:59:59.000Z In my React App, I aim to showcase both thi ...

Removing an item from JSON data using Node.js and Express

Currently, I am attempting to remove an entry from json data. In order to view the data, I utilize the following: app.route('/data/:id') .get((req:Request, res: Response) => { let id = req.params.id; res.status(200).send(projects ...

Closing a Bootstrap 5 Dropdown in React without using toggle feature

Currently, I am using a search field in my application that displays data as dropdown items when the user is typing. The library being utilized is React Bootstrap (bootstrap 5) and it's functioning perfectly. However, an issue arises where the dropdow ...

Tips for preventing special characters from being entered into a Kendo grid input column

Is there a way to prevent users from entering special characters into the description column of my Kendo grid? The column field setup is as follows: { field : "myDesc", width : 200, title : "My Description"} I have attempted the following approach so far ...

What is the impact of memory on NodeJS performance?

Currently delving into a book on NodeJS, I stumbled upon an intriguing example: const express = require('express') const bodyParser = require('body-parser') const app = express() var tweets = [] app.listen('8000', '172 ...

Error: Unhandled promise rejection - The method this.~(...) does not support chaining with .then() function

I'm currently working on a project that involves fetching data from a database. I am attempting to call the function getUserById() (which will use axios to communicate with the database later), and I'm encountering an issue with the line this.get ...

managing browser pop-ups in selenium with the help of JavaScript

My objective is to input a username and password in the popup box that shows up every time the page loads. I am utilizing selenium for this purpose, and unfortunately, all attempts I've made so far have been unsuccessful. I attempted to use the follo ...

Storing API request results in local storage with React does not automatically cache them

I want to create a basic component using react that can fetch an image from an API and save it for future requests within the application. However, I'm facing an issue where the component renders every time it's used, resulting in multiple API ca ...

Turn off the background color box with a single click

Whenever I click on a header menu item, the background changes along with it. Is there a way to prevent this from happening? https://i.stack.imgur.com/p6bJ9.png Take a look here ...

Like the Word outline view, the list view can be easily collapsed and expanded with a simple click

I've seen a few examples, but I haven't been able to achieve what I'm looking for. I need to extract text from a field and convert it into an outline view similar to the one in Word. Is this possible? Any help would be greatly appreciated. I ...

How can you alter a property within an array of records retrieved from a Mongoose query?

const images = await tbl .find({ creator_id: req.user._id, }) .select({ creator_id: 0, }) .exec() .then((images) => images.forEach((image) => { image.file_name = process.env.IMAGE_ ...

Navigating and Organizing in Ionic Service Factory

Apologies for the beginner question. I am looking to incorporate filtering and sorting by name on my webpage. However, I have encountered two issues after attempting to implement this functionality using a factory in services.js: When typing a search ter ...

Is there a way to direct Webpack in a Next.JS application to resolve a particular dependency from an external directory?

Is it possible to make all react imports in the given directory structure resolve to react-b? |__node_modules | |__react-a | |__app-a | |__component-a | |__next-app | |__react-b | |__component-b // component-a import { useEffect } from ' ...

Encountering an error with Next-Slicezone module integration with Prismic.io: "module parse failed"

I'm feeling lost and frustrated as I struggle to figure out what's going wrong. The documentation for slicezone in nextjs is not very clear, and the Prismic Community board hasn't been very helpful either. Can someone please assist me with t ...

Executing code on the server-side (back-end) using Javascript: A step-by-step guide

Imagine wanting to execute JavaScript on the server side instead of exposing the code to the client. For example: Starting with the JS native window Object from the browser. Performing all JavaScript processing in the backend by passing the window Object ...

The ESLint tool seems to be struggling to detect the package named "@typescript-eslint/eslint-plugin"

Struggling with getting ESLint to function properly on a new Angular project in VS Code. The error message I keep encountering is about failing to load "@typescript-eslint/eslint-plugin". After spending the past 3 hours troubleshooting, I have searched hig ...

Can you explain the distinction between angular-highcharts and highcharts-angular?

Our team has been utilizing both angular-highcharts and highcharts-angular in various projects. It appears that one functions as a directive while the other serves as a wrapper. I'm seeking clarification on the distinctions between the two and recomme ...

Creating a responsive div section following the navigation bar using Bootstrap

Seeking advice on how to position a div region containing page content beneath a navbar without using margin-top. Currently, I have implemented margin-top:200px, but I am uncertain if this approach is responsive or if there is a better method available. T ...

The error "Call to a member function exports() on array" occurs when attempting to use the

As I attempt to send an array of values to the jobExport() collection, I encounter an error stating Call to a member function jobsExport() on array. I comprehend the necessity for the collection to be populated with modal collection value. However, my goal ...

The Fetch API within DatoCMS isn't able to retrieve all published posts

I am trying to retrieve all of my published blog posts from DatoCMS. Currently, I have implemented the code below. The issue I am facing is that it only retrieves 65 posts when I know I have more than 100... Can anyone help me figure out what I might be d ...