文本超出行数限制显示‘展开/收起’+移动端兼容
前端文本内容展示,无论是在web端还是移动端,都是最基础的内容之一。对于成百上千字数的文本,web端可以使用css简单操作,将超出行数部分进行截断显示成‘…‘,然后在hover/click时弹出全部内容即可勉强满足要求,但对移动端来说这种操作就行不通了。css操作美不美观先不说,不同的手机型号、不同的浏览器及版本光是兼容问题就能使人头皮发麻,那么怎么在移动端文本较多时巧妙而优雅的实现展开/收起功能呢?
思路(参考借鉴antdMobile中的ellipsis组件):
前面说到使用css控制行数截断会有兼容问题,且展开/收起按钮很难做到紧挨着结束文本。那么想要做到高贵而优雅,通过js截取的方式来实现效果就是一个很不错的思路了:
-
拿到要展示文本区域的dom元素,通过 getComputedStyle 获取该dom的所有样式;
-
创建一个额外的不在可视范围内的div,让div的样式与获取的dom元素样式完全一样(用于获取实际截断后的文本);
-
页面展示文本的最大高度 maxHeight 为 行高*行数 (行高是1中拿到的 lineHeight,行数由实际需求而定)有 padding-top 或 padding- bottom 时加上;
-
遍历文本,逐个将文本中的文字放到2中的div内,每放一次获取当前div的高度与 maxHeight 进行比较,当放置最后一个文字,div当前高度大于maxHeight 时,前面的截断部分就是行数限制内需要展示的全部文字(逐字计算高度性能很差,此处使用二分法效果绝佳);
-
获取到目标截断文本后,将2中的工具人div删除,此时将前面得到的文本展示在页面中即可。
纸上得来终觉浅,此时应该上硬菜~:
<div ref={
ref => this.domRef = ref}>{
ellipsised}
<a>{
expended?'收起':'展开'}</a>
</div>
calcEllipsis = (cntxt, rows) => {
// 获取目标元素,创建工具人
const originStyle = window.getComputedStyle(this.domRef),
container = document.createElement('div'),
styleNames = Array.prototype.slice.apply(originStyle);
// 将目标dom的样式复制到工具人上
styleNames.forEach(name => {
container.style.setProperty(name, originStyle.getPropertyValue(name));
});
// 将工具人div放在可视范围之外
container.style.position = 'fixed';
container.style.left = '20222222px';
container.style.top = '20222222px';
container.style.zIndex = '-2022';
container.style.height = 'auto';
container.style.minHeight = 'auto';
container.style.maxHeight = 'auto';
container.style.textOverflow = 'clip';
container.style.whiteSpace = 'normal';
container.style.webkitLineClamp = 'unset';
container.style.display = 'block';
const lineHeight = this.pxToNumber(originStyle.lineHeight),
paddingTop = this.pxToNumber(originStyle.paddingTop),
paddingBottom = this.pxToNumber(originStyle.paddingBottom),
maxHeight = Math.floor(lineHeight *