文本溢出截断的几种方案

文本溢出截断的几种方案

单行截断

单行截断只需要使用 CSS 就可以实现,并且省略号位置和文本衔接得很好,也不存在兼容性问题。

white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

多行截断

多行截断比较戳中痛点,那就是兼容性不好,先看看官方的多行截断方法。

display: -webkit-box;
overflow: hidden;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;

之前在公司使用到多行截断,在 Chrome、Firefox 和 safari 上都可以得到良好的支持,但在 IE11 上就不行了。因为 IE 识别不了 -webkit-line-clamp 这个 css 属性(详情看这里),所以只能通过其他方式来模拟一下多行截断了。

伪元素 + 绝对定位

通过伪元素 + 绝对定位来模拟多行截断原理其实很简单,就只是使用伪元素来表示省略号… ,并通过定位来将它放到文末就行了。但还需要固定文本框的高度来控制要显示的行数。

.ie-polyfills {
    height: 4.5rem;
    position: relative;
  word-break: break-all;
  overflow: hidden;
}

.ie-polyfills::after {
    content: '...';
    position: absolute;
    bottom: 0;
    right: 0;
    padding-left: 15px;
  width: 18px;
  /* 使用渐变让 ... 过渡地自然点 */
  background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1) 50%);
}

使用这种方法来模拟多行截断缺点也很明显,就是因为是手动控制 … 的位置,所以文本和 … 之间的过渡就没有原生方法那么自然,文字可能还会漏出一点出来。并且无法识别文本的长短,无论文本是否溢出都会显示省略号。

所以必须保证文本一定会溢出才行。

目前只发现在 IE 上无法识别 -webkit-line-clamp,所以我们大可不必只为了兼容 IE 就放弃原生的多行截断方法。我们可以判断当前用户使用的浏览器来选择使用哪种截断方式。如果是 IE 浏览器(navigator.userAgent.indexOf('Trident') > -1) 的话,我们就采用模拟截断的 CSS 样式,否则就使用原生的截断方法。

伪元素 + float

利用浮动的特性,当文本高度不超过指定最大高度时,省略号(右浮动)会位于文本的下方,因为超出隐藏所以省略号不可见。但文本高度超过指定最大高度时,省略号向右浮动贴着文本的左边线,此时再通过定位把省略号拉到文本右边即可。通过这种方法的好处在于可以根据文本超出与否来控制是否显示省略号,缺点也是文本和省略号之间过渡不好。

<div class="app">
  <p class="text">多行截断多行截断多行截断多行截断多行截断多行截断多行截断多行截断多行截断多行截断多行截断多行截断多行截断多行截断多行截断多行截断多行截断</p>
</div>
p {
  margin: 0;
}
.app {
  margin: 0;
  width: 200px;
  max-height: 40px;
  line-height: 20px;
  overflow: hidden;
  border: 1px solid;
}
.app::before{
  float: left;
  content:'';
  width: 20px;
  height: 40px;
}
.app .text {
  float: right;
  width: 100%;
  margin-left: -20px;
  word-break: break-all;
}
.app::after{
  float:right;
  content:'...';
  width: 20px;
  height: 20px;
  position: relative;
  left:100%;
  transform: translate(-100%,-100%);
  background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1) 50%);
}

js截断文本并拼接省略号

通过 js 来模拟截断,主要是要判断文本字符串在哪个位置超出了限定的行数,并在该位置上截取出字符串并拼接省略号。通过 getComputedStyle 获取文本单行的宽度,并除以文本的字体大小,就可以得到每行可以容纳的字数,再乘上要显示的行数就是在限定的行数内可以显示的最多字体数了。

这种方法可以避免文本和省略号过渡不自然的问题,并且只有在文本超出的时候才会显示省略号进行截断。然而缺陷在于中英文的字体大小即使保持一致(假设是16px),但中英文的字体宽度不一样,导致计算后英文每行实际的显示字数比计算值大很多,省略号显示的位置会有所偏差。可以在这里进行实验。

const ele = document.getElementsByClassName('text')[0];
let text = ele.innerText;
const totalTextLen = text.length;
function formatStr() {
  const lines = 2; // 要显示的行数
  const fontSize = parseInt(window.getComputedStyle(ele).fontSize); // 字体大小
  // 单行文本的长度,使用getComputedStyle计算出来的长度带了单位,需要去掉单位
  const lineWidth = parseInt(window.getComputedStyle(ele).width); 
  const lineStrNum = Math.floor(lineWidth / fontSize); // 一行可容纳的字数(不区分中英文)
  let content = '';
  const totalStrLen = Math.floor(lineStrNum * lines); // lines行可以容纳的字数
  // 文本的长度超出了lines行可以容纳的字数长度,则在超出的地方进行截断,并拼接上省略号
  if (totalTextLen > totalStrLen) {
    content = text.substring(0, totalStrLen - 1).concat('...');
  } else {
    content = text;
  }
  ele.innerText = content;
}

formatStr();
window.onresize = () => {
  formatStr();
};

Clamp.js

对文本进行截断的工具:Clamp.js,可以选择对指定的行数进行截断、截断后插入的字符 / HTML代码。

CDN引入

基本使用方法:

var ele = document.getElementById('text');
$clamp(ele, {
  clamp: '2',
});    

 上一篇
React Hook 中的闭包问题 React Hook 中的闭包问题
React Hook 中的闭包问题本文不对 React Hook 做过多的介绍,只是记录笔者在学习过程中遇到的问题。关于 React Hook 的介绍请参考官方文档,或者也可以看我的个人笔记。 闭包直接开门见山,通过代码来看问题。 fun
2019-11-16
下一篇 
Git常用命令 Git常用命令
在头条实习的这八周里,刚开始没少因为我对 Git 掌握严重不足而挨 mentor 训过,所以在这里记录一些比较常用的 Git 操作。 合并多个commit假如我们现在在本地仓库提了3个 commit,就像这样: 如果我们直接这样 pus
2019-09-03
  目录