【问题标题】:React show Material-UI Tooltip only for text that has ellipsisReact 仅针对具有省略号的文本显示 Material-UI 工具提示
【发布时间】:2019-10-28 12:39:55
【问题描述】:

正在寻找一种方法让 material-ui 的工具提示扩展表格单元格中的文本如果文本被省略号截断(溢出)。 p>

目前在我的表格中,我有一个像这样的单元格:

<TableCell className={classes.descriptionCell}>{row.description}</TableCell>

我对 descriptionCell 的样式是这样的:

    descriptionCell: {
        whiteSpace: 'nowrap',
        maxWidth: '200px',
        overflow: 'hidden',
        textOverflow: 'ellipsis'
    }

这使文本在此表中按我希望的方式运行,但我希望能够悬停并在工具提示中查看其余部分,最好是 Material-UI 的内置工具提示组件。

我知道这里存在一个包https://www.npmjs.com/package/react-ellipsis-with-tooltip 应该这样做,但它使用引导工具提示,而不是材料 UI。

【问题讨论】:

标签: reactjs react-redux tooltip material-ui ellipsis


【解决方案1】:

定义文本是否溢出的方法在接受的答案中有缺陷。由于scrollWidthclientWidth 返回四舍五入的整数值,当它们之间的差异很小时,我们将得到相等的值,并且工具提示将不起作用。问题是省略号也算作clientWidth,所以当我们只有一个或多个字符溢出时,我们会看到省略号,但scrollWidthclientWidth 会相等。 以下是我用分数精度确定scrollWidthclientWidth 并解决了这个问题的解决方案:

import React, { useRef, useState, useEffect } from 'react';
import { Tooltip } from '@material-ui/core';

const OverflowTooltip = ({ children }) => {
    const textElementRef = useRef();
    
    const checkOverflow = () => {
        // Using getBoundingClientRect, instead of scrollWidth and clientWidth, to get width with fractional accuracy
        const clientWidth = textElementRef.current.getBoundingClientRect().width

        textElementRef.current.style.overflow = 'visible';
        const contentWidth = textElementRef.current.getBoundingClientRect().width
        textElementRef.current.style.overflow = 'hidden';

        setIsOverflow(contentWidth > clientWidth);
    }
    
    useEffect(() => {
        checkOverflow();
        window.addEventListener('resize', checkOverflow)
        return () => {
            window.removeEventListener('resize', checkOverflow)
        }
    }, []);
    
    const [isOverflowed, setIsOverflow] = useState(false);
    
  return (  
    <Tooltip title={children} disableHoverListener={!isOverflowed}>       
      <span ref={textElementRef}
            style={{
                whiteSpace: 'nowrap',
                overflow: 'hidden',
                textOverflow: 'ellipsis',
            }}
        >
            {children}
      </span>
    </Tooltip>
  );
};
export default OverflowTooltip

【讨论】:

    【解决方案2】:

    基于@Dheeraj 的回答 - 这是非常接近他的组件但在类型脚本版本和更有意义的道具名称:

    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;
    

    我们这样使用它:

    <OverflowTooltip 
        tooltip={'tooltip message here'}
        text={'very long text here'}
    />
    

    【讨论】:

      【解决方案3】:

      根据 benjamin.keen 的回答,这是他的代码的功能版本:

      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>
        );
      };
      

      【讨论】:

        【解决方案4】:

        离开@benjamin.keen 回答。这是一个独立的功能组件,它只是他使用钩子执行比较功能的答案的扩展。

        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;
        

        【讨论】:

        • 您应该通过从第一个钩子返回 () =&gt; { window.removeEventListener('resize', compareSize); }useEffect 钩子组合成一个钩子。
        • 检查元素mouseover 的大小可能更有意义,因为即使窗口没有改变其大小,内容也会溢出。
        • 我对这个解决方案有一个奇怪的问题,返回的clientWidth 是错误的,并且总是与scrollWidth 相同,除非我在 div 中放置了一些静态内容。如果我至少包含一些静态字符,它可以正常工作。可能是什么问题?这让我发疯了
        • 我想出了上面的问题。事实证明,由于省略号包含在 clientWidth 中,因此对于某些内容 clientWidth == scrollWidth 即使由于舍入而使用省略号。此处描述了此问题:github.com/w3c/csswg-drafts/issues/4123
        【解决方案5】:

        我今天遇到了同样的问题,@vijay-menon 的回答非常有帮助。这是一个用于同一事物的简单独立组件:

        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>
                );
            }
        }
        

        示例用法:

        <OverflowTip>
              some long text here that may get truncated based on space
        </OverflowTip>
        

        令人讨厌的是,如果页面中元素的空间动态变化(例如页面大小调整或动态 DOM 变化),它不会确认新空间并重新计算它是否溢出。

        Tippy 等其他工具提示库有一个在尝试 打开工具提示时触发的方法。这是进行溢出检查的理想场所,因为无论文本元素的 DOM 宽度是否发生变化,它都将始终有效。不幸的是,使用 Material UI 提供的 API 会更麻烦。

        【讨论】:

          【解决方案6】:

          请在下面找到代码框 - https://codesandbox.io/s/material-demo-p2omr

          我在这里使用 ref 获取 TableCell DOM 节点,然后比较 scrollWidth 和 clientWidth 以确定是否必须显示 Tooltip。(这是基于答案 here

          我已将“rowref”(具有 ref 的属性)和“open”(禁用/启用工具提示)作为新属性添加到行中。我不知道您的数据来自哪里,但我假设您可以将这些属性添加到行中。

          还有一点需要注意,我只是将“disableHoverListener”属性设置为禁用 tooltip 。还有其他道具 - “disableFocusListener”和“disableTouchListener”,如果你想使用它们。更多信息here

          希望这对你有用。如果您对代码有任何疑问,请告诉我。

          【讨论】:

          • 这将无条件地执行工具提示。问题是询问如何仅在文本溢出时显示工具提示。
          • @RyanCogswell 是的,完全正确。仅当文本并非总是溢出时才需要它。如果还没有,我会在问题中更清楚地说明这一点。
          • 我对要求的理解有误。我删除了我的原始答案并添加了一个新答案。让我知道这是否有效。
          • @dave99collins,如果这对你有用,请告诉我。
          猜你喜欢
          • 2019-01-06
          • 2011-07-25
          • 2019-05-28
          • 2021-10-31
          • 2013-10-08
          • 1970-01-01
          • 2019-06-06
          • 1970-01-01
          相关资源
          最近更新 更多