组件-多行文本省略-展开收起

1、功能

需要实现下面的功能,多行文本出现省略,并且提供展开和收起按钮。

2、技术方案与实现

1、用-webkit-line-clamp 实现

-webkit-line-clamp 指定显示几行,然后display、overflow等等设置一下就好了。

省略号的位置非常正确,但是问题是兼容性一般,想在省略号后面加按钮就有点困难。

css 复制代码
.text {
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

2、用float手动实现

  1. 提供一个省略号的dom,然后设置为右浮动;
  2. text-container容器设置一个伪元素挤压文字和省略号,之后再让文字content上去(margin-top为负值)

问题:当一个单词不够显示距离时会完全消失,导致省略号和文本有很长的一段距离,可以用文本的截断解决,但是英文状态会把单词给截断,这样也不好。这个问题可以用绝对定位来解决,但是可能出现省略号遮住文字的问题。。。

代码如下:

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      .text-container {
        width: 200px;
        height: 100px;
        overflow: hidden;
      }
      .text-container::before {
        content: '';
        display: block;
        height: 80px;
      }
      .content {
        margin-top: -80px;
        line-height: 24px;
      }
      .more {
        margin-right: 10px;
        float: right;
        line-height: 1;
      }
    </style>
    <title>Document</title>
  </head>
  <body>
    <div class="text-container">
      <div class="more">...</div>
      <div class="content">
        瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁😀😍瑟尔华润国际hi哦平台警示牌日哦和家人屁
      </div>
    </div>
  </body>
</html>

效果如下:

3、react-组件

tsx:

javascript 复制代码
import React, { memo, useEffect, useRef, useState } from 'react';
import CollapseText from './components/CollapseText';
import ExpendText from './components/ExpendText';
import styles from './index.scss';

/**
 * 可展开文本组件的Props接口
 */
interface Props {
  /** 要显示的文本内容 */
  text: string;
  /** 最大显示行数,超出后会截断显示省略号,默认为3行 */
  maxLines?: number;
  /** 自定义CSS类名 */
  className?: string;
  /** 收起按钮的文本,默认为"收起" */
  collapseText?: string | React.ReactNode;
  /** 展开按钮的文本,默认为"展开" */
  expandText?: string | React.ReactNode;
  /** 展开/收起回调 */
  onToggleExpansion?: (isExpanded: boolean) => void;
}

/**
 * 可展开文本组件
 *
 * 功能:
 * - 根据指定行数限制文本显示
 * - 超出行数时自动截断并显示展开按钮
 * - 支持展开/收起功能
 * - 支持自定义按钮文本
 *
 * @param props - 组件属性
 * @returns React函数组件
 */
const ExpandableText: React.FC<Props> = ({
  text,
  maxLines = 3,
  className = '',
  collapseText = <CollapseText />,
  expandText = <ExpendText />,
  onToggleExpansion,
}) => {
  // 控制文本是否展开的状态
  const [isExpanded, setIsExpanded] = useState(false);
  // 标记文本是否需要展开功能(即是否超出了指定行数)
  const [needsExpansion, setNeedsExpansion] = useState(false);
  // 文本容器的DOM引用,用于计算文本高度
  const textRef = useRef<HTMLDivElement>(null);
  const [peerContainerHeight, setPeerContainerHeight] = useState(0);
  const [containerHeight, setContainerHeight] = useState(0);
  /**
   * 检测文本是否超出指定的行数
   * 通过比较实际内容高度和最大允许高度来判断
   */
  useEffect(() => {
    if (textRef.current) {
      // 获取当前元素的行高
      const lineHeight = parseInt(getComputedStyle(textRef.current).lineHeight);
      // 计算指定行数对应的最大高度
      const maxHeight = lineHeight * maxLines;
      setPeerContainerHeight(maxHeight);
      setContainerHeight(maxHeight);
      // 获取实际内容的滚动高度(包含被隐藏的部分)
      const actualHeight = textRef.current.scrollHeight;
      // 如果实际高度超过最大高度,则需要展开功能
      setNeedsExpansion(actualHeight > maxHeight);
    }
  }, [text, maxLines]); // 当文本内容或最大行数改变时重新检测

  /**
   * 切换文本展开/收起状态
   * @param e - 鼠标点击事件
   */
  const toggleExpansion = (e: React.MouseEvent<HTMLButtonElement>) => {
    // 阻止事件冒泡,避免触发父级元素的点击事件
    e.stopPropagation();
    const newIsExpanded = !isExpanded;
    // 切换展开状态
    setIsExpanded(newIsExpanded);
    onToggleExpansion?.(newIsExpanded);
    // 切换展开状态时,更新容器高度
    if (newIsExpanded) {
      setContainerHeight(textRef.current?.clientHeight || containerHeight);
    } else {
      setContainerHeight(peerContainerHeight);
    }
  };

  return (
    <div
      className={`${styles['text-container']} ${className}`}
      style={
        {
          '--container-height': `${containerHeight}px`,
          '--line-height': `${24}px`,
        } as React.CSSProperties
      }
    >
      {needsExpansion && (
        // <span className={isExpanded ? styles.more : styles['more-position-expend']}>
        <span className={styles['more']}>
          {!isExpanded && <span className={styles['more-text']}>... </span>}
          <span className={styles['more-button']} onClick={toggleExpansion}>
            {isExpanded ? collapseText : expandText}
          </span>
        </span>
      )}
      <div ref={textRef} className={`${styles['content']}`}>
        {text}
      </div>
    </div>
  );
};

export default memo(ExpandableText);

css:

javascript 复制代码
.text-container {
  height: var(--container-height);
  overflow: hidden;
  position: relative;
}
.text-container::before {
  content: '';
  display: block;
  height: calc(var(--container-height) - var(--line-height, 24px));
}

.content {
  margin-top: calc((var(--container-height) - var(--line-height, 24px)) * -1);
  line-height: var(--line-height, 24px);
}
.more-position-expend {
  padding-left: 10px;
  position: absolute;
  bottom: 4px;
  right: 0;
  background: white;
}
.more {
  float: right;
  line-height: var(--line-height, 24px);
}
.more-text {
  font-size: 16px;
}
.more-button {
  margin-left: 4px;
  cursor: pointer;
  color: rgb(255, 34, 102);
}

3、END

目前采用float的方式

相关推荐
知识分享小能手7 小时前
微信小程序入门学习教程,从入门到精通,微信小程序常用API(上)——知识点详解 + 案例实战(4)
前端·javascript·学习·微信小程序·小程序·html5·微信开放平台
清灵xmf7 小时前
CSS field-sizing 让表单「活」起来
前端·css·field-sizing
文火冰糖的硅基工坊7 小时前
[光学原理与应用-480]:《国产检测设备对比表》
前端·人工智能·系统架构·制造·半导体·产业链
excel7 小时前
Qiankun 子应用生命周期及使用场景解析
前端
weixin_446260858 小时前
Django - 让开发变得简单高效的Web框架
前端·数据库·django
蓝莓味的口香糖8 小时前
【JS】什么是单例模式
开发语言·javascript·单例模式
ObjectX前端实验室9 小时前
【react18原理探究实践】异步可中断 & 时间分片
前端·react.js
SoaringHeart9 小时前
Flutter进阶:自定义一个 json 转 model 工具
前端·flutter·dart
努力打怪升级9 小时前
Rocky Linux 8 远程管理配置指南(宿主机 VNC + KVM 虚拟机 VNC)
前端·chrome
brzhang9 小时前
AI Agent 干不好活,不是它笨,告诉你一个残忍的现实,是你给他的工具太难用了
前端·后端·架构