theme: fancy
都快2023年了…
身为一个前端,多行文本溢出省略号显示是基本上都会遇到的一个问题,网上随便一搜就能找到纯CSS的解决方案,类似如下:
.ellipsis {
overflow : hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
抛开兼容性不谈(感觉也不太需要考虑兼容性了),似乎已经相当完美解决了。
不过从产品角度来看,可以省略来优化展示,但是==信息不能丢啊==!
这也就是说,需要在信息被裁减(有省略号)时,有Tooltip
之类的交互可以让用户看到全部内容。
这个其实也简单,给Html
标签加上title
属性即可,类似:
<div title="这个是title的样式显示">
鼠标在这段文字上停留一下
</div>
这样就可以看到效果了:
不过这个效果么,差强人意啊。一个是得鼠标在这里悬停一会儿才会出现,另外就是样子也不太好看。
展示更好的效果,其实ant-design
已经实现了(https://ant.design/components/typography#components-typography-demo-ellipsis),类似如下:
有信息被裁减时,鼠标移上去,可以自动显示Tooltip
来展示更详细的内容。
那既然ant-design
都已经实现了,直接拿来用不就可以了?
因为……这个能力在ant-design
的v4
版本才有,而我现在所处的项目还在使用v3
版本!另外,ant-design
的v5
版本这几天也发布了!
直接升级ant-design
估计是不太可能的了,得从长计议,只能在这个低版本上想想办法了。
需求是什么?
既然只能自己实现,那就先弄清楚核心需求是什么。其实也简单:
- 设定多行限制,文案能够全部展示,则正常展示,无
Tooltip
- 若文案在多行限制时,无法展示完全,则显示省略号,并有
Tooltip
想好了需求,那就去借鉴(抄)一下ant-design
是怎么实现的。一看之下,发现代码量还是有一些的,不过主要是在实现一些不是我所关注的功能点,弄过来太重了,不太容易借鉴啊,还是得靠自己。
怎么实现呢?
从需求分析来看,核心是需要区分什么时候会需要裁剪。最直观的做法就是在不限制行数时,先让其渲染一遍,看看是否超出预定行数,超出则表示需要裁切。
那么就可以先简单搭建一下这个组件的框架:(为啥还是Class Component
?因为是…老项目)
// 多行省略显示的样式
const baseStyle = {
overflow : 'hidden',
textOverflow: 'ellipsis',
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
}
class ToolTipEllipsisWord extends React.Component {
state = {
// 是否需要展示 tooltip
isNeedTooltip: false,
}
componentDidMount() {
// TODO 判断 isNeedTooltip 是否需要更改为 true
}
render() {
const { children, tooltip, rows = 2 } = this.props;
const node = children || tooltip;
if (this.state.isNeedTooltip) {
return (
<Tooltip title={tooltip}>
<div style={{ ...baseStyle, WebkitLineClamp: rows}}>
{node}
</div>
</Tooltip>
);
}
// 首次直接渲染
return <div>{node}</div>;
}
}
而componentDidMount
正好是在首次渲染后执行,正好来处理是否需要裁切。
然后来判断首次渲染是否超出预定高度。这里涉及两个信息的获取:
- 一个是如何获取渲染高度,这个可以取
DOM
的clientHeight
即可 - 而预定高度则是
lineHeight * rows
这里有个小难点就是如何获取lineHeight
,这不得借鉴(抄)一下,别的仓库是怎么写的。
翻了一下Github
,找到一个简单的(https://github.com/josephschmitt/Clamp.js),核心是使用window
的getComputedStyle
来获取真实的lineHeight
:
function computeStyle(elem, prop) {
return window.getComputedStyle(elem, null).getPropertyValue(prop);
}
function getLineHeight(elem) {
var lh = computeStyle(elem, 'line-height');
if (lh == 'normal') {
// Normal line heights vary from browser to browser. The spec recommends
// a value between 1.0 and 1.2 of the font size. Using 1.1 to split the diff.
lh = parseInt(computeStyle(elem, 'font-size')) * 1.2;
}
return parseInt(lh);
}
那么再把逻辑加一下:
class ToolTipEllipsisWord extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
componentDidMount() {
if (this.myRef.current) {
const ele = this.myRef.current;
const lineHight = getLineHeight(ele);
const maxHeight = lineHight * this.props.rows;
if (maxHeight < ele.clientHeight) {
this.setState({
isNeedTooltip: true,
})
}
}
}
// 一些逻辑
render() {
// 一些逻辑
return <div ref={this.myRef}>{node}</div>;
}
}
这样就轻松搞定了,看下效果(可以在这里看 https://codepen.io/waiter951/pen/mdKqoYR):
代码片段
然后呢?
这个只是一个Demo
级别的代码,使用上会有诸多限制,比如:
- 后续
props
改变之后,并没有重新判断是否需要Tooltip
,这块的话可以在componentDidUpdate
中来做额外处理 - 如果区域大小变化之后,也没有相关的处理
- 还有记得加上
word-break: break-all;
,毕竟如果不会换行,也就不会超过高度限制了
总体来说,如果场景简单的话,这样使用也就够了,再复杂的,后续升级ant-design
吧。
暂无评论内容