组件-多行文本省略-展开收起
1、功能
需要实现下面的功能,多行文本出现省略,并且提供展开和收起按钮。
2、技术方案与实现
1、用-webkit-line-clamp 实现
-webkit-line-clamp 指定显示几行,然后display、overflow等等设置一下就好了。
省略号的位置非常正确,但是问题是兼容性一般,想在省略号后面加按钮就有点困难。
.text {overflow: hidden;display: -webkit-box;-webkit-line-clamp: 3;-webkit-box-orient: vertical;
}
2、用float手动实现
- 提供一个省略号的dom,然后设置为右浮动;
- text-container容器设置一个伪元素挤压文字和省略号,之后再让文字content上去(margin-top为负值)
问题:当一个单词不够显示距离时会完全消失,导致省略号和文本有很长的一段距离,可以用文本的截断解决,但是英文状态会把单词给截断,这样也不好。这个问题可以用绝对定位来解决,但是可能出现省略号遮住文字的问题。。。
代码如下:
<!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:
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 (<divclassName={`${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:
.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的方式