js如何实现当文本内容超过三行时,第三行尾部显示省略号

1,552 阅读2分钟

1、背景

前一阵子做需求,产品经理提了这样一个需求:当文本宽度过宽的时候,可以换行,如果换行超过三行,第三行最后面显示省略号。

当时我的内心:

image.png

2、实现

2.1 css实现

css中有一个-webkit-line-clamp属性,具体作用是:可以将块的内容限制在指定的行数内。

它接受一个整数,用来表明最大显示行数,取值范围:[1,正无穷]

要使此属性生效需要设置以下两个属性:

p {
  display: -webkit-box 使容器成为一个弹性盒容器。
  -webkit-box-orient: vertical 设置盒子的排列方向为纵向。
}

完整实现如下:

<p>
  In this example the <code>-webkit-line-clamp</code> property is set to
  <code>3</code>, which means the text is clamped after three lines. An ellipsis
  will be shown at the point where the text is clamped.
</p>
​
p {
  width: 300px;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

但是目前这个属性浏览器支持度不好,不具有实际的使用价值。

image-20240620160322042.png

2.2 js实现 - 滚动高度

原理:通过循环比较所有文本占据的高度scrollHeight和允许的行数所计算出来的最大高度maxHeight,如果scrollHeight 大于 maxHeight,则将删除文本尾部的一个字符。

核心逻辑如下:

    function getLineHeight(ele) {
      let lh = window
        .getComputedStyle(ele, null)
        .getPropertyValue("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(
            window.getComputedStyle(ele, null).getPropertyValue("font-size")
          ) * 1.2;
      }
      return parseInt(lh);
    }
    function clampText(element, lines) {
      const lineHeight = getLineHeight(element);
      const maxHeight = lineHeight * (lines + 1);
      let text = element.innerText;
      let originalText = element.getAttribute("data-original-text") || text;
​
      // 如果当前文本已经是截断的,尝试恢复原始文本
      if (text.endsWith("...") && element.scrollHeight < maxHeight) {
        element.innerText = originalText;
        text = element.innerText;
      }
​
      // 截断文本
      while (element.scrollHeight > maxHeight) {
        text = text.substring(0, text.length - 1);
        element.innerText = text + "...";
      }
​
      // 保存原始文本
      if (!element.hasAttribute("data-original-text")) {
        element.setAttribute("data-original-text", originalText);
      }
    }

核心的逻辑就是这一段:

  // 截断文本
  while (element.scrollHeight > maxHeight) {
    text = text.substring(0, text.length - 1);
    element.innerText = text + "...";
  }

需要注意的是:由于获取line-heigth的时候可能获取不到确切的数值,所以需要额外处理一下,但是不同浏览器也是有不同的差异的,

最终效果如下图:

image-20240620172950832.png

如果有容器大小缩放的场景,还需要使用ResizeObserver来监听容器宽度的变化,来动态调整行数。

const ele = document.querySelector("p");
      const observer = new ResizeObserver((entries) => {
        for (let entry of entries) {
          clampText(entry.target, 3);
        }
      });
observer.observe(ele);

完整实现如下:

3、使用clamp.js

Clamp.js是一个实现line-clamp的库,能够方便的设置折断的行数,同时也能够设置动画来拆分耗时计算(通过setTimeout实现)。其原理也是通过计算行高容器高度实现的。

使用:

//一行
$clamp(myHeader, {clamp: 1});
​
//多行
$clamp(myHeader, {clamp: 3});
​
//基于容器高度自动计算
$clamp(myParagraph, {clamp: 'auto'});
​
//固定高度
$clamp(myParagraph, {clamp: '35px'});

image-20240620175543656.png

4、相关阅读

js如何实现当文本内容过长时,中间显示省略号...,两端正常展示

line-clamp