前端实现多行文本折叠

前言

多行文本折叠在前端是一个很常见的交互, 如下图微博展示,当内容超过一定行数,会显示省略号 和"全文"按钮。那么该如何通过实现这样的效果呢?本文将介绍两种实现方案。

要实现这样的效果,主要分为两个部分:

  • 1.当文案超过一定行数时截断内容并显示省略号
  • 2.当内容超过行数时显示操作按钮

文案超过行数显示省略号

要实现文案超过行数显示省略号,只需要以下短短的几行代码即可实现:

xml 复制代码
<style>
  .text-container {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
    text-overflow: ellipsis;
  }
</style>

<div class="text-container">
  这里是一段可能比较长的文字,用来测试效果是否正确。
  这里是一段可能比较长的文字,用来测试效果是否正确。
  这里是一段可能比较长的文字,用来测试效果是否正确。
</div>

实现效果:

内容超过行数时显示操作按钮

上面通过 CSS 实现了超出内容的截断,但是是否显示"操作"按钮取决于当前内容是否存在截断。因此,需要先实现当前内容是否存在截断的判断。

当前内容是否存在截断

通过观察元素结构发现,如果当前内容存在截断,文案的实际内容高度比显示的文案内容高度高;否则就是没有发生内容截断。

根据上述判断,通过 js 实现当前内容是否存在截断的判断,代码如下:

ini 复制代码
const textContainer = document.querySelector(".text-container");
const checkEllipsis = () => {
	const range = document.createRange();
  range.setStart(textContainer, 0);
  range.setEnd(textContainer, textContainer.childNodes.length);
  let rangeHeight = range.getBoundingClientRect().height;
  let textContainerHeight = textContainer.getBoundingClientRect().height;

  if (rangeHeight > textContainerHeight) {
    console.log("当前内容存在截断");
  } else {
    console.log("当前内容没有截断");
  }
}

此处用到了一个特殊的 API :createRange, 用于创建一个 Range 对象,表示文档中的一个范围。Range 对象通常用于选择文档中的一部分内容,然后对其进行操作。

操作按钮放置在下一行

接下来,就是放置操作按钮,正常应该把按钮放置在省略号右边,这样实现起来稍有难度,暂且放一放。先实现一个简单的效果,就是把按钮放置在下一行,具体实现代码如下:

xml 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <meta name="description" content="baru belajar html" />
    <title>Hello World!</title>
    <style>
      .text-container {
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 3;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .more-link {
        display: none;
      }
    </style>
  </head>
  <body>
    <div class="text-container">
      这里是一段可能比较长的文字,用来测试效果是否正确。
      这里是一段可能比较长的文字,用来测试效果是否正确。
      <!-- 这里是一段可能比较长的文字,用来测试效果是否正确。 -->
    </div>
    <a href="#" class="more-link">详情</a>

    <script>
      const textContainer = document.querySelector(".text-container");
      const moreLink = document.querySelector(".more-link");

      const checkEllipsis = () => {
        const range = document.createRange();
        range.setStart(textContainer, 0);
        range.setEnd(textContainer, textContainer.childNodes.length);
        let rangeHeight = range.getBoundingClientRect().height;
        let textContainerHeight = textContainer.getBoundingClientRect().height;

        if (rangeHeight > textContainerHeight) {
          console.log("当前内容存在截断");
          moreLink.style.display = "inline-block";
        } else {
          console.log("当前内容没有截断");
          moreLink.style.display = "none";
        }
      };
      checkEllipsis();
    </script>
  </body>
</html>

实现效果:

按钮放置在省略号右边

实现思路:在文案元素中增加一个按钮元素,并设置按钮为浮动,这样文案就是环绕在按钮边上,然后设置按钮位置在右下角。在设置浮动元素定位的时候,要借助一个伪元素来布局,并清除浮动元素。

xml 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <meta name="description" content="baru belajar html" />
    <title>Hello World!</title>
    <style>
      .text-container {
        display: -webkit-box;
        -webkit-line-clamp: 3;
        -webkit-box-orient: vertical;
        overflow: hidden;
      }
      .text-container::before {
        content: "";
        float: right;
        width: 0px;
        height: 100%;
        margin-bottom: -28px;
        /* background: red; */
      }
      .wrap {
        display: flex;
      }
      .more-link {
        display: none;
        float: right;
        clear: both;
      }
    </style>
  </head>
  <body>
    <div class="wrap">
      <div class="text-container">
        <button class="more-link">展开</button>
        浮动元素是如何定位的 正如我们前面提到的那样,当一个元素浮动之后,
        它会被移出正常的文档流,然后向左或者向右平移,一直平移直到碰到了所处的容器的边框,或者碰到另外一个浮动的元素。
      </div>
    </div>

    <script>
      const textContainer = document.querySelector(".text-container");
      const moreLink = document.querySelector(".more-link");
      if (textContainer.offsetHeight > textContainer.clientHeight) {
        moreLink.style.display = "inline-block";
      }

      const checkEllipsis = () => {
        const range = document.createRange();
        range.setStart(textContainer, 0);
        range.setEnd(textContainer, textContainer.childNodes.length);
        let rangeHeight = range.getBoundingClientRect().height;
        let textContainerHeight = textContainer.getBoundingClientRect().height;

        if (rangeHeight > textContainerHeight) {
          console.log("当前内容存在截断");
          moreLink.style.display = "inline-block";
        } else {
          console.log("当前内容没有截断");
          moreLink.style.display = "none";
        }
      };
      checkEllipsis();
    </script>
  </body>
</html>

实现效果:

参考

CSS 实现多行文本"展开收起"

JS如何判断文字被ellipsis了?

相关推荐
军军君0112 分钟前
Three.js基础功能学习十五:智能黑板实现实例二
开发语言·前端·javascript·vue.js·3d·threejs·三维
IT枫斗者20 分钟前
构建具有执行功能的 AI Agent:基于工作记忆的任务规划与元认知监控架构
android·前端·vue.js·spring boot·后端·架构
hotlinhao21 分钟前
Nginx rewrite last 与 redirect 的区别——Vue history 模式短链接踩坑记录
前端·vue.js·nginx
ZC跨境爬虫23 分钟前
海南大学交友平台开发实战day7(实现核心匹配算法+解决JSON请求报错问题)
前端·python·算法·html·json
下北沢美食家27 分钟前
CSS面试题2
前端·css
weixin_4617694033 分钟前
npm create vue@latest 错误
前端·vue.js·npm
WindrunnerMax34 分钟前
从零实现富文本编辑器#13-React非编辑节点的内容渲染
前端·架构·github
四千岁35 分钟前
Ollama+OpenWebUI 最佳组合:本地大模型可视化交互方案
前端·javascript·后端
写不来代码的草莓熊37 分钟前
el-date-picker ,自定义输入数字自动转换显示yyyy-mm-dd HH:mm:ss格式
前端·javascript·vue.js
ssshooter37 分钟前
Tauri 应用苹果签名踩坑实录
前端·架构·全栈