React show Material-UI Tooltip only for text that has ellipsis
Solution 1
To go off of @benjamin.keen answer. Here is a standalone functional component which is just an extension of his answer using hooks to perform the comparison functions.
import React, { useRef, useEffect, useState } from 'react';
import Tooltip from '@material-ui/core/Tooltip';
const OverflowTip = props => {
// Create Ref
const textElementRef = useRef();
const compareSize = () => {
const compare =
textElementRef.current.scrollWidth > textElementRef.current.clientWidth;
console.log('compare: ', compare);
setHover(compare);
};
// compare once and add resize listener on "componentDidMount"
useEffect(() => {
compareSize();
window.addEventListener('resize', compareSize);
}, []);
// remove resize listener again on "componentWillUnmount"
useEffect(() => () => {
window.removeEventListener('resize', compareSize);
}, []);
// Define state and function to update the value
const [hoverStatus, setHover] = useState(false);
return (
<Tooltip
title={props.value}
interactive
disableHoverListener={!hoverStatus}
style={{fontSize: '2em'}}
>
<div
ref={textElementRef}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
}}
>
{props.someLongText}
</div>
</Tooltip>
);
};
export default OverflowTip;
Solution 2
based on benjamin.keen answer, this is the functional version of his code:
import React, { useRef, useState, useEffect } from 'react';
import Tooltip from '@material-ui/core/Tooltip';
const OverflowTip = ({ children }) => {
const [isOverflowed, setIsOverflow] = useState(false);
const textElementRef = useRef();
useEffect(() => {
setIsOverflow(textElementRef.current.scrollWidth > textElementRef.current.clientWidth);
}, []);
return (
<Tooltip title={children} disableHoverListener={!isOverflowed}>
<div
ref={textElementRef}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{children}
</div>
</Tooltip>
);
};
Solution 3
Please find the codesandbox below - https://codesandbox.io/s/material-demo-p2omr
I am using ref here to get the TableCell DOM Node and then comparing the scrollWidth and clientWidth to determine if Tooltip has to be displayed.(This is based on answer here)
I have added "rowref" (property that has the ref) and "open" (disable/enable tooltip) as new properties to the rows. I don't know where your data is coming from, but I am assuming you can add these properties to the row.
One more thing to note, I am only setting "disableHoverListener" prop to disable tooltip . There are other props - "disableFocusListener" & "disableTouchListener" , If you want to use those. More info here
Hope this works out for you. Let me know if you have any doubts in the code.
Solution 4
I ran into this same problem today and @vijay-menon's answer was very helpful. Here's a simple standalone component for the same thing:
import React, { Component } from 'react';
import Tooltip from '@material-ui/core/Tooltip';
class OverflowTip extends Component {
constructor(props) {
super(props);
this.state = {
overflowed: false
};
this.textElement = React.createRef();
}
componentDidMount () {
this.setState({
isOverflowed: this.textElement.current.scrollWidth > this.textElement.current.clientWidth
});
}
render () {
const { isOverflowed } = this.state;
return (
<Tooltip
title={this.props.children}
disableHoverListener={!isOverflowed}>
<div
ref={this.textElement}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
}}>
{this.props.children}
</div>
</Tooltip>
);
}
}
Example usage:
<OverflowTip>
some long text here that may get truncated based on space
</OverflowTip>
The one nuisance is that if the space for the element dynamically changes in the page (e.g. page resize or dynamic DOM change) it won't acknowledge the new space and recompute whether it's overflowed.
Other tooltip libraries like Tippy have a method that's fired when trying to open the tooltip. That's a perfect place to do the overflow check because it'll always work, regardless if the DOM width had changed for the text element. Unfortunately it's fussier to do that with the API provided by Material UI.
Solution 5
based on @Dheeraj answer - this is the very close to his component but in type script version and more makes sense props names:
import React, { useRef, useEffect, useState } from 'react';
import Tooltip from '@material-ui/core/Tooltip';
interface Props {
tooltip: string;
text: string;
}
const OverflowTooltip = (props: Props) => {
const textElementRef = useRef<HTMLInputElement | null>(null);
const compareSize = () => {
const compare =
textElementRef.current.scrollWidth > textElementRef.current.clientWidth;
setHover(compare);
};
useEffect(() => {
compareSize();
window.addEventListener('resize', compareSize);
}, []);
useEffect(() => () => {
window.removeEventListener('resize', compareSize);
}, []);
const [hoverStatus, setHover] = useState(false);
return (
<Tooltip
title={props.tooltip}
interactive
disableHoverListener={!hoverStatus}
>
<div
ref={textElementRef}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{props.text}
</div>
</Tooltip>
);
};
export default OverflowTooltip;
and we use it like this:
<OverflowTooltip
tooltip={'tooltip message here'}
text={'very long text here'}
/>
dave99collins
Updated on June 13, 2022Comments
-
dave99collins about 2 years
Looking for a way to have material-ui's tooltip expand the text in a table cell ONLY if the text is cut off with an ellipsis (overflowing).
Currently in my table I have a cell like this:
<TableCell className={classes.descriptionCell}>{row.description}</TableCell>
and my styling for descriptionCell is like this:
descriptionCell: { whiteSpace: 'nowrap', maxWidth: '200px', overflow: 'hidden', textOverflow: 'ellipsis' }
This makes the text behave the way I would like it to in this table, but I want to be able to hover and view the rest of it in a tooltip, preferably Material-UI's built in tooltip component.
I know there is a package that exists here https://www.npmjs.com/package/react-ellipsis-with-tooltip which should do this, BUT it uses bootstrap tooltip, not material UI.
-
Ryan Cogswell about 5 yearsThis will do the tooltip unconditionally. The question is asking how to only have the tooltip when the text overflows.
-
dave99collins about 5 years@RyanCogswell Yes, exactly. Need it only when text overflows not all the time. I'll make that more clear in the question if it wasn't already.
-
Vijay Venugopal Menon about 5 yearsI understood the requirement incorrectly. I have removed my original answer and added a new answer. Let me know if this works.
-
Vijay Venugopal Menon about 5 years@dave99collins, please let me know if this worked for you .
-
STiLeTT over 3 yearsYou should combine
useEffect
hooks into single one by returning() => { window.removeEventListener('resize', compareSize); }
from the first one. -
xReeQz about 3 yearsIt probably makes more sense to check the size on the element's
mouseover
because the content can overflow even if the window hasn't changed its size. -
Yuriy almost 3 yearsI have a weird issue with this solution,
clientWidth
returned is wrong and always the same asscrollWidth
, unless I put some static content inside the div. If I include at least some static character, it works correctly. What could be the issue? It is driving me crazy -
Yuriy almost 3 yearsI figure the issue above. It turns out that since ellipsis is included in clientWidth, for some content clientWidth == scrollWidth even with ellipsis due to rounding. This issue is described here: github.com/w3c/csswg-drafts/issues/4123