Enhancing React components with ellipsis and tooltips for text longer than two lines

Can a React component be developed to automatically add an ellipsis after two lines and display a tooltip only when the text is wrapped?

I attempted to customize the Material UI's Typography component with the "noWrap" property and additional CSS, but was unsuccessful. You can view my attempt here.

I would greatly appreciate any assistance in achieving a design similar to the following screen: https://i.sstatic.net/6m6c4.png

Answer №1

There are two primary components to address in this issue:

  • Implementing ellipsis for text overflow based on vertical overflow to allow for multiple lines without unlimited expansion
  • Detecting vertical overflow and incorporating Tooltip functionality in such cases

I am skeptical that the solution proposed by FrankerZ will effectively display the ellipsis. From what I gather, text-overflow: ellipsis typically caters to horizontal overflow which necessitates confining the text to a single line.

I stumbled upon a method for achieving ellipsis on vertical overflow here. However, extensive adjustments might be needed when integrating this approach within Material UI components (such as ListItem) that introduce additional CSS rules, potentially leading to complexity that outweighs the benefits. This solution involves allocating space for the ellipsis at the end of each text line, which may not be ideal. While there are alternative solutions available, I have personally not experimented with any besides this one.

The second aspect (detecting overflow) appears to be more straightforward and can be managed using

divRef.current.scrollHeight > divRef.current.offsetHeight
. This method was derived from various sources suggesting a similar approach based on width. Although I have only applied this technique today, it seems functional (although I have not extensively tested it across different browsers aside from Chrome).

The example provided utilizes hooks for syntax convenience. If you are not using the alpha version, you will need to translate the state, ref, and effect mechanisms into their class-based equivalents. Additionally, the current implementation does not address window resizing, which would require a reassessment of the Tooltip's necessity. With these considerations in mind, this code snippet aims to guide you towards a viable solution.

Below is the code snippet:

import React, { useRef, useState, useEffect } from "react";
import ReactDOM from "react-dom";
import Tooltip from "@material-ui/core/Tooltip";
import { withStyles } from "@material-ui/core/styles";
/* 
CSS from http://hackingui.com/front-end/a-pure-css-solution-for-multiline-text-truncation/ 
Additional syntax help from https://stackoverflow.com/questions/40965977/cant-target-before-pseudo-selector-in-jss
*/
const styles = theme => ({
  listItem: {
    maxWidth: "20rem",
    overflow: "hidden",
    position: "relative",
    lineHeight: "1.2em",
    maxHeight: "2.4em",
    textAlign: "justify",
    marginRight: "-1em",
    paddingRight: "1em",
    borderBottom: "1px solid",
    marginBottom: "0.5em",
    "&&:before": {
      content: '"..."',
      position: "absolute",
      right: 0,
      bottom: 0
    },
    "&&:after": {
      content: '""',
      position: "absolute",
      right: 0,
      width: "1em",
      height: "1em",
      marginTop: "0.2em",
      background: "white"
    }
  }
});
const data = [
  "Some short text",
  "Some text that is a little bit longer",
  "Some text that will need to wrap but still fits on two lines",
  "Some text that will overflow because it is more than just two lines worth when the maxWidth is set at 20rem.",
  "A massive range of hammer drill machines and rotary hammers for SDS-plus accessory tools, designed for higher performance drilling and longer life - for easy drilling in concrete and other materials."
];
const TooltipDiv = props => {
  const divRef = useRef(null);
  const [allowTooltip, setAllowTooltip] = useState(false);
  useEffect(() => {
    if (
      !allowTooltip &&
      divRef.current.scrollHeight > divRef.current.offsetHeight
    ) {
      setAllowTooltip(true);
    }
  }, []);
  if (allowTooltip) {
    return (
      <Tooltip title={props.text}>
        <div ref={divRef} className={props.className}>
          {props.text}
        </div>
      </Tooltip>
    );
  }
  return (
    <div ref={divRef} className={props.className}>
      {props.text}
    </div>
  );
};
function App(props) {
  return (
    <>
      {data.map(text => {
        return (
          <>
            <TooltipDiv text={text} className={props.classes.listItem} />
          </>
        );
      })}
    </>
  );
}
const StyledApp = withStyles(styles)(App);
const rootElement = document.getElementById("root");
ReactDOM.render(<StyledApp />, rootElement);

You can view the code in action here.

Answer №2

To achieve this, you can make use of CSS in the following way:

.tooltip {
    overflow: hidden;
    text-overflow: ellipsis;
    height: /* Adjust the height to display just 2 lines of text */
}

For additional information and alternative methods, you can refer to this resource.

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

The website experiences a sudden crash shortly after launching, displaying the error message "EADDRINUSE" for port 80

PROBLEM I have a react-based website running on a node-express server. My backend server is working fine on port 3000, but the website on port 80 keeps crashing. When I use pm2 to start my website (https://www.edvicer.com) with the command pm2 start serv ...

Issue of displaying buttons based on sibling's height under certain conditions

OBJECTIVE I have undertaken a project to enhance my skills in React and TypeScript by developing a UI chat interface. The design requirement is that when a chat message has enough vertical space, its action buttons should appear stacked vertically to the ...

Issue: "TypeError: Unable to retrieve dynamically imported module" encountered while utilizing dynamic imports in Remix application

I am currently facing an issue with dynamic imports in my Remix application. Whenever I try to dynamically import a module using the code below: const module = await import(../lib/lang/lang-${language.toLowerCase()}.js); An error occurs: TypeError: Fail ...

How can you ensure that text in a container automatically moves to a new line and causes the container to expand downward if necessary, when

Are you searching for a way to make text inside a container wrap to a new line and have the container expand downwards if needed? Update <div class="modal hide fade" id="modalRemoveReserve" style="display:none;"> <div class="modal-header"&g ...

Adjustable text size to accommodate different cultural changes

My team is working on an MVC web application, with CSS stylesheets utilized in views. We are trying to achieve the following scenario: Whenever the language culture changes from English to another language, we want the font size of a specific class to ...

Exploring the world of Material UI Autocomplete with the power of react-virtuoso virtualization

Currently, I have a nearly functioning version of the Material UI Autocomplete with a virtualized dropdown list using react-virtuoso. The problem that I am encountering is when moving upwards in the dropdown list (as shown at 0:25 in the video), the list ...

Angular 2: Dealing with Missing Image Loading

When viewing an HTML file containing the following code: <div> <img src="New-Google-Logo.png"/> </div> The image file New-Google-Logo.png is expected to load from the same folder as the HTML file. However, after running ng s ...

Exploring an Array in React js with hooks to locate a specific id

I am looking to update my page featuring a list of product cards. My goal is to transition from using class components to functional components when clicking on a product card to be directed to the specific product detail page. products.json { "shoe ...

Maximizing cell background color in HTML table with limited width fails to function properly

Having difficulty turning the header cells (th) with background-color applied? It seems like the color bar may be too small, the cell width too large, or the background isn't being affected properly. Is there a way to create a narrow cell with long t ...

The query executed with UseApolloClient does not fetchMore results

I'm currently working on a project using Apollo on the client side and utilizing react-apollo-hooks. However, I've encountered an issue with useApolloClient. When firing a query with my client, I don't receive all the data I need when using ...

The Bootstrap v5 dropdown feature is malfunctioning as the drop-down menu fails to appear

I've been struggling to create a dropdown menu using Bootstrap, but it doesn't seem to be working as expected. Despite that, I have no issues utilizing the framework for styling purposes. The Bootstrap js link is placed at the bottom of the body ...

Having trouble incorporating Bootstrap 5's SASS variables into my project's SASS files

I'm attempting to utilize Bootstrap 5 variables in my custom styles.scss file. I have integrated the entire Bootstrap 5 source code into my project. I have noticed that I need to import specific .scss files in my styles.scss file for it to function co ...

Resolving the problem with iterating through $list using SCSS/SASS @mixin

Consider the following list: $box-shadow-properties: -webkit-box-shadow, -moz-box-shadow, box-shadow; I am attempting to create a mixin that iterates through this list: @mixin box-shadow-with-inset($box-shadow-params, $inset-params) { @each $property ...

Adjust the position of an unordered list element in CSS to move it lower on

I have a ul element in my navigation bar at the top of the webpage that I want to adjust slightly downwards. Take a look at the current navigation bar: image To align the text with the white bar at the top of the page, I need to move it down just a bit. ...

Unable to tune in to live public events using Laravel, ReactJs, and Soketi

I've been working on setting up a chat application where I need to capture a NewMessage event from my backend built with Laravel, and display those events in my frontend which is a vite-react-app. To achieve this, I have a soketi docker image running ...

Troubleshooting: Resolving the Error "Cannot find Property htmlFor on Custom React Component"

I am currently working with a custom component that looks like this: import React from "react"; import classnames from 'classnames'; import { ButtonVariantColor } from '../types/button'; export type IconButtonProps = { e ...

Having trouble with Jquery CSS transform not functioning properly in Internet Explorer 8?

When it comes to utilizing CSS3 features that are not supported by older browsers such as IE8, I tend to apply them using jQuery instead. However, I have noticed that the CSS transform in jQuery does not work... Here is my code: http://codepen.io/vincentc ...

Role in HTML/CSS

I'm currently facing an issue with getting my footer to stick to the bottom of the page. In Internet Explorer, it appears centered under the rest of the content instead of at the very bottom. When I try to view it in Chrome, it ends up positioned next ...

Navigating in React Navigation: Techniques to Directly Access a Specific Index in a Datasource

I am a beginner at React Native and I have been working on developing a simple recipe app. Everything was going well until I reached the point where I needed to navigate to a specific recipe from my dataset. I am looking to create a singleRecipe.js compone ...

Issue with hydration in Next.js while trying to access the persisted "token" variable in Zustand and triggering a loading spinner

Below is the code snippet from _app.tsx where components/pages are wrapped in a PageWrapper component that handles displaying a loading spinner. export default function App(props: MyAppProps) { const updateJWT = useJWTStore((state) => state.setJWT); ...