翼度科技»论坛 编程开发 JavaScript 查看内容

使用JavaScript实现文本收起展开(省略)功能

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
省略号,作为一种常见的文本处理方式,在很多情况下都十分常见。特别是当我们需要在省略号后面添加额外文字时,这种需求更是不少见。

然而,仅仅依赖 CSS 来实现兼容性、流畅的文本省略效果是不现实的。就拿 Vant4TextEllipsis 文本省略功能来说,我们可以从中学到不少。
参考Vant4:TextEllipsis 文本省略,为大家分析一波案例(感觉麻烦可以去看下面的附带代码)。
在整个代码中,关键之处在于使用了 tail、middleTail 等关键手段。我们着重讨论 tail,即在文本尾部插入内容的操作。

简单的CSS隐藏

大家或多或少都曾编写过 CSS 省略文本的代码。比如,要将文本限制在四行内显示,可以使用以下 CSS 代码:
隐藏为四行
  1. overflow: hidden;
  2. display: -webkit-box;
  3. -webkit-box-orient: vertical;
  4. -webkit-line-clamp: 4; /* 控制显示行数 */
复制代码


其中的限制

尽管如此,这种方法有着一定的局限性:

  • 如何在省略号后添加展开、收起等文字?
  • 添加后,如何保证文字与省略号在同一行?
  • 展开后,如何保证文字在合适位置显示?
纯 CSS 可以实现这些效果,但实际操作起来会比较困难。
既然 CSS 不够理想,我们可以考虑使用 JavaScript 来实现这些功能。
首先,让我们来分析一波:

  • 收起时保留的高度是多少?
  • 如何截取超出高度的文字内容?
  • 如何确保截取后的文字内容不影响原来的渲染和展示?
完成以上三点,我们就完成了任务。

开始分析
  1. <div class="root">
  2.     散文是一种文学形式,与之相对的称为韵文或诗文。散文可分为广义与狭义的解释,其中,广义散文最容易辨识与定义的方式,是“松散”的结构。也就是说,扣除其它文学形式重叠部分,运用普通语法结构,不讲究音韵,不讲究排比,没有任何束缚及限制的文字梳理方式,都可称为散文。除此,狭义散文是单指文学范畴内,结构松散之非韵文作品。文学专指的散文,历代作品有着各时代不同流变的脉络,而正因为松散带来的自由,散文作品表达出的思想通常有着丰富与圆满的特色
  3. </div>
复制代码
  1. <style>
  2.   .root {
  3.     line-height: 1.6;
  4.     overflow-wrap: break-word;
  5.     word-break: break-all;
  6.   }
  7. </style>
复制代码
首先解决:收起时要保留的高度是多少?
假设我们保留4行:
需要给予文本限高 :
  1. line-height: 1.6;
复制代码
  1. row = 4
  2. const maxHeight = Math.ceil(
  3.     (Number(rows) + 0.5) * pxToNum(lineHeight) + "一些其他的容器高度:比如padding"
  4. );
复制代码
在这个计算中,0.5 是一个调整值,用于在计算最大高度时考虑到文本行数的不确定性。这个调整值的目的是为了确保即使行数计算稍微超过了预期的行数,也能够提前截断文本,以防止文本超出容器的高度限制。
得到了高度,如何切割文字
注意看:
  1. tail()
复制代码
函数是一个递归函数,用于在给定的容器高度限制下,计算文本的截断位置。让我来详细解释一下
  1. tail()
复制代码
函数的工作原理:
  1. const tail = (left, right) => {

  2.     if (right - left <= 1) {
  3.       return content.slice(0, left) + dots;
  4.     }
  5.    
  6.     const middle = Math.round((left + right) / 2);
  7.     container.innerText = content.slice(0, middle) + dots + actionText;
  8.    
  9.     if (container.offsetHeight > maxHeight) {
  10.       return tail(left, middle);
  11.     }
  12.     return tail(middle, right);
  13.   };

  14.   container.innerText = tail(0, end);
复制代码
当递归结束时,函数会返回最终的截断位置,即最佳的截断位置。
递归都有自己结束判断,这个递归的结束点就在
  1. right - left <= 1
复制代码
,
计算中间位置
  1. middle
复制代码
,并将文本内容从左边界到中间位置加上省略符号,然后设置到容器元素中
检查容器元素的高度是否超过了指定的最大高度限制。如果超过了,说明当前截断位置过早,需要继续向左边界靠近进行截断;否则,说明当前截断位置过晚,需要继续向右边界靠近进行截断。
最终找到,合适的middle值,在一起返回
  1. content.slice(0, left) + dots
复制代码
作为结点的
  1. innerText
复制代码
文字。
期间,一直在收缩,就算高度相同也在收缩右边界的内容,
  1. right - left <= 1
复制代码
的作用就是刚刚好实现的截断位置以满足给定的最大高度限制,不会超出限制。
总结:这个
  1. tail()
复制代码
函数使用递归的方式,根据给定的容器高度限制,计算出文本的截断位置。递归的结束判断在
  1. right - left <= 1
复制代码
,通过不断调整
  1. middle
复制代码
的值,最终找到合适的截断位置以满足给定的最大高度限制,不会超出限制。
在切割下,保证文字内容不影响原来的渲染展示
为了不影响原文本的渲染展示,我们使用了克隆节点的方式。这里参考了 Vant 中的代码,并做了适当修改。
修改成了,更加适合JS宝宝体质的代码
  1. const rootNode = document.querySelector(".root");

  2. const cloneContainer = () => {
  3.   // cloneContainer就是为了得到文本的高度
  4.   if (!rootNode || !rootNode.isConnected) return;

  5.   const originStyle = window.getComputedStyle(rootNode);
  6.   const container = document.createElement('div');
  7.   const styleNames= Array.prototype.slice.apply(originStyle);

  8.   styleNames.forEach((name) => {
  9.     container.style.setProperty(name, originStyle.getPropertyValue(name));
  10.   });

  11.   container.style.position = 'fixed'; // 不在文档流中
  12.   container.style.zIndex = '-9999'; // 看不到
  13.   container.style.top = '-9999px'; // 看不到
  14.   container.style.height = 'auto';
  15.   container.style.minHeight = 'auto';
  16.   container.style.maxHeight = 'auto';

  17.   container.innerText = content;
  18.   document.body.appendChild(container);

  19.   return container;
  20. };
复制代码
解析:
  1. window.getComputedStyle
复制代码
是一个用于获取指定元素的所有计算样式的方法。它返回一个
  1. CSSStyleDeclaration
复制代码
对象,其中包含了指定元素的所有计算样式属性及其对应的值。

实现效果



实例代码

html
  1. <style>
  2.   .root {
  3.     padding: 12px;
  4.     line-height: 1.6;
  5.     overflow-wrap: break-word;
  6.     word-break: break-all;
  7.   }
  8. </style>

  9. <body>
  10.   <div class="root">
  11.     散文是一种文学形式,与之相对的称为韵文或诗文。散文可分为广义与狭义的解释,其中,广义散文最容易辨识与定义的方式,是“松散”的结构。也就是说,扣除其它文学形式重叠部分,运用普通语法结构,不讲究音韵,不讲究排比,没有任何束缚及限制的文字梳理方式,都可称为散文。除此,狭义散文是单指文学范畴内,结构松散之非韵文作品。文学专指的散文,历代作品有着各时代不同流变的脉络,而正因为松散带来的自由,散文作品表达出的思想通常有着丰富与圆满的特色
  12.   </div>
  13. </body>
  14. <script src="TextEllipsis.js"> </script>
复制代码
JS代码
  1. document.addEventListener("DOMContentLoaded", function() {  let expanded =true;let collapseText = "收起";let expandText = "展开";const rows = 3;let actionText = expanded ? collapseText : expandText;let hasAction = false;const rootNode = document.querySelector(".root");const content = rootNode.innerText;let text = "";const dots = "...";const pxToNum = (value) => {  if (!value) return 0;  const match = value.match(/^\d*(\.\d*)?/);  return match ? Number(match[0]) : 0;};const cloneContainer = () => {  // cloneContainer就是为了得到文本的高度  if (!rootNode || !rootNode.isConnected) return;  const originStyle = window.getComputedStyle(rootNode);  const container = document.createElement('div');  const styleNames= Array.prototype.slice.apply(originStyle);  styleNames.forEach((name) => {    container.style.setProperty(name, originStyle.getPropertyValue(name));  });  container.style.position = 'fixed';  container.style.zIndex = '-9999';  container.style.top = '-9999px';  container.style.height = 'auto';  container.style.minHeight = 'auto';  container.style.maxHeight = 'auto';  container.innerText = content;  document.body.appendChild(container);  return container;};const calcEllipsised = () => {  const calcEllipsisText = (    container,    maxHeight  ) => {    const end = content.length;    const calcEllipse = () => {      const tail = (left, right) => {        if (right - left <= 1) {          return content.slice(0, left) + dots;        }        const middle = Math.round((left + right) / 2);        container.innerText = content.slice(0, middle) + dots + actionText;        if (container.offsetHeight > maxHeight) {          return tail(left, middle);        }        return tail(middle, right);      };      container.innerText = tail(0, end);    };    calcEllipse();    return container.innerText;  };  const container = cloneContainer();  if (!container) {    needRecalculate = true;    return;  }  let { paddingBottom, paddingTop, lineHeight } = container.style;  const maxHeight = Math.ceil(    (Number(rows) + 0.5) * pxToNum(lineHeight) +      pxToNum(paddingTop) +      pxToNum(paddingBottom),  );  if (maxHeight < container.offsetHeight) {    hasAction = true;    text = calcEllipsisText(container, maxHeight);  } else {    hasAction = false;    text = content;  }  document.body.removeChild(container);};const toggle = (isExpanded = !expanded) => {  expanded = isExpanded;  actionText = expanded ? collapseText : expandText;};const renderAction = () => {  calcEllipsised();  const container = document.createElement('span');  container.classList.add("expend_txt");  container.addEventListener('click',()=>{    toggle();    container.innerText = hasAction ? actionText : null;    rootNode.innerText = expanded ? content : text;    rootNode.appendChild(container);  });  container.innerText = hasAction ? actionText : null;;  rootNode.appendChild(container);}renderAction()})
复制代码
以上就是使用JavaScript实现文本收起展开(省略)功能的详细内容,更多关于JavaScript文本展开收起的资料请关注脚本之家其它相关文章!

来源:https://www.jb51.net/javascript/319079m22.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具