来源 | https://wintc.top/article/58多行文本超过指定行数隐藏超出部分并显示“...查看全部”是一个常遇到的需求,网上也有人实现过类似的功能,不过还是想自己写写看,于是就写了一个vue的组件,本文简单介绍一下实现思路。遇到这个需求的同学可以尝试一下这个组件,支持npm安装使用:组件地址:https://github.com/Lushenggang/vue-overflow-ellipsis
在线体验:https://wintc.top/laboratory/#/ellipsis

一、需求描述

长度不定的一段文字,最多显示n行(比如3行),不超过n行正常显示;超过n行则在最后一行尾部显示“展开”或“查看全部”之类的按钮,点击按钮则展开显示全部内容,或者跳转到其它页面展示所有内容。预期效果如下:

二、实现原理

纯css很难完美实现这个功能,所以还得借助js来实现,实现思路大体相似,都是判断内容是否超过指定行数,超过则截取字符串的前x个字符,然后然后和“...查看全部”拼接在一起,这里的x即截取长度,需要动态计算。想通过上述方案实现,有几个问题需要解决:

  • 怎样判断文字是否超过指定行数

  • 如何计算字符串截取长度

  • 动态响应,包括响应页面布局变动、字符串变化、指定行数变化等

下面具体研究一下这些问题。

1、怎样判断一段文字是否超过指定行数?

首先解决一个小问题:如何计算指定行数的高度?我首先想到的是使用textarea的rows属性,指定行数,然后计算textarea撑起的高度。另一个方法是将行高的计算值与行数相乘,即得到指定行数的高度,这个办法我没尝试过,但是想必可行。解决了指定行数高度的问题,计算一段文字是否超过指定行数就很容易了。我们可以将指定行数的textarea使用绝对定位absolute脱离文档流,放到文字的下方,然后通过文本容器的底部与textarea的底部相比较,如果文本容器的底部更靠下,说明超过指定行数。这个判断可以通过getBoundingClientRect接口获取到两个容器的位置、大小信息,然后比较位置信息中的bottom属性即可。可以这样设计DOM结构:

 
{{ showContent }} ... 查看更多

然后使用css控制textarea,使其脱离文档流并且不能被看到以及被触发鼠标事件等(textarea标签中的readonly以及tabIndex属性是必要的):

.ellipsis-container  text-align left  position relative  line-height 1.5  padding 0 !important  .textarea-container    position absolute    left 0    right 0    pointer-events none    opacity 0    z-index -1    textarea      vertical-align middle      padding 0      resize none      overflow hidden      font-size inherit      line-height inherit      outline none      border none

2、如何计算字符串截取长度x——双边逼近法(二分思想)

只要可以判断一段文字是否超过指定行数,那我们就可以动态地尝试截取字符串,直到找到合适的截断长度x。这个长度满足从x的位置截断字符串,前半部分+“...查看全部”等文字刚好不会超出指定行数N,但是多截取一个字,则会超出N行。最直观的想法就是直接遍历,让x从0开始增长到显示文本总长度,对于每个x值,都计算一次文字是否超过N行,没超过则加继续遍历,超过则获得了合适的长度x - 1,跳出循环。当然也可以让x从文本总长度递减遍历。不过这里最大的问题在于浏览器的回流和重绘。因为我们每次截取字符串都需要浏览器重新渲染出来才能得到是否超过N行,这过程中就触发了浏览器的重绘或回流,每次循环都会触发一次。而对于正常的需求来说,假设N取值是3,那很可能每次计算会导致50次以上的重绘或回流,这中间消耗的性能还是非常大的,不小心可能就是几十毫秒甚至上百毫秒。这个计算过程应该在一个任务(即常说的”宏任务“)中完成,否则计算过程中会出现显示闪动的”异常“情况,所以可以说计算过程是阻塞的,因此计算的总时间一定要控制到非常低,即要减少计算的次数。可以考虑使用"双边逼近法"(或称”二分法“)查找合适的截取长度x,大大减少尝试的次数。第一次先以文本长度为截取长度,计算是否超过N行,没超过则停止计算;超过则取1/2长度进行截取,如果此时没超过N行,则在1/2长度到文本长度之间继续二分查找,如果超过则在0到1/2文本长度中继续二分查找。直到查找区间开始值与结束值相差为1,则开始值即为所求。具体实现可以看下文中的完整代码。

3、监听页面变动

对于vue项目来说,传入组件的字符串、行数等可能随时改变,可以watch这些属性变化,然后重新计算一次截取长度。另一方面,对于页面布局而言,可能会因为其它页面元素的增删或者样式改变,导致页面布局变动,影响到文本容器的宽度,此时也应该重新计算一次截取长度。监听文本容器宽度的变化,可以考虑使用ResizeObserver来监听,但是这个接口的兼容性不够好(IE各个版本都不支持),因此选择了一个npm库element-resize-detector来监测(非常好用)。

三、代码实现

完整的代码实现如下:

  
{{ showContent }} {{ ellipsisText }} {{ btnText }}

在代码实现中refresh函数用于计算截取长度,在文本内容、rows属性等发生改变或者文本容器尺寸改变时将被调用。每次refresh调用会异步地递归调用多次checkLoop,refresh可能重新调用,新的refresh调用将结束之前的checkLoop的调用。

四、其它

1、支持html串的考虑

现在的实现方案并不支持内容是html文本,如果需要支持HTML文本,问题将复杂许多。主要在于HTML字符串的解析和截断,不像文本字字符串那么简单。不过或许可以借助浏览器的Range API 来实现截断位置的定位,Range的insertNode以及setStart接口可以将“...查看全部”插入到指定位置,而如果插入位置刚好符合需要,则可以通过Range.cloneContents()")接口取得截取HTML字符串的相关内容,理论上是可行的,不过具体细节以及处理效率得实践后才知道。

2、减少浏览器回流的影响

上述实现方案中,每一次截取都需要浏览器重新渲染DOM,即重绘。重绘的影响还比较小,而如果截取的字符串行数发生改变,还会引发文本容器的高度变化,这时候就会导致浏览器回流,而文本容器在文档流中,回流将会影响整个文档。想解决这个问题,可以使用一个脱离文档流的元素来进行字符串动态截断后的渲染与判断,布局就类似上述的textarea。因为不在文档流中,回流的影响范围就会减少到该元素自身。获得截断长度后再截断文本,渲染到真正的文本容器即可。本文仅作为一个简单的原理概述的示例,没有做这个处理,对具体细节感兴趣的同学,可以查看github仓库代码。

android怎么截取接口返回html代码中的内容_如何实现文本内容折叠并显示“...查看全部”?...相关推荐

  1. 【Android NDK 开发】在 C 代码中获取 Android 系统信息 ( NDK 项目创建 | NDK 配置 | 获取 Android 系统版本号 )

    文章目录 I . 创建 NDK 项目 II . NDK 项目 相关配置 III . NDK 中获取 Android 版本号 IV . 使用 __system_property_get 可获取的参数 I ...

  2. android 动态设边距,在代码中动态设置页边距 - android

    我有一个只包含ScrollView和TableLayout的活动.在我的代码我加入tablerows,一切工作正常,除了利润 - 它不工作(没有任何反应),下面是代码:在代码中动态设置页边距 - an ...

  3. JMeter从HTTP接口返回的参数中获取数据 - 使用Json提取器

    Http接口返回的数据,一般都是json格式的,如果需要提取出其中的某个数据,作为后续其他接口的入参,那么可以使用json提取器. 一.从接口中提取一个数据 以登录接口为例,当登录成功后接口会返回一个 ...

  4. 调用后台接口返回报错前端隐藏提示_前端异常监控解决方案研究(转)

    前端监控包括行为监控.异常监控.性能监控等,本文主要讨论异常监控.对于前端而言,和后端处于同一个监控系统中,前端有自己的监控方案,后端也有自己等监控方案,但两者并不分离,因为一个用户在操作应用过程中如 ...

  5. php接口返回一个数组怎末写_返回php数组

    PHP数组简介 v 一.PHP数组的分类 按照下标的不同,PHP中的数组分为关联数组和索引数组: 索引数组:下标从0开始,依次增长. $arr=[1,2,3,4,5]; 关联数组:下标为字符串格式,每 ...

  6. c# mysql代码中写事务_代码中添加事务控制 VS(数据库存储过程+事务) 保证数据的完整性与一致性...

    [c#]代码库代码中使用事务前提:务必保证一个功能(或用例)在同一个打开的数据连接上,放到同一个事务里面操作. 首先是在D层添加一个类为了保存当前操作的这一个连接放到一个事务中执行,并事务执行打开同一 ...

  7. android:editable=quot;falsequot;,如何在代码中复制android:editable =“false”?

    我认为拒绝所有更改的InputFilter是一个很好的解决scheme: editText.setFilters(new InputFilter[] { new InputFilter() { pub ...

  8. 调用后台接口返回报错前端隐藏提示_从零开始的Android新项目9 - 前端用后台接口设计...

    这回来讲讲后台接口的设计. 可能有同学会觉得后台的接口和我们大前端开发有什么关系?试想一下,在碰到一些不合理的接口设计的时候,你们开发是否觉得很别扭--需要为了坑爹的接口写很多脏代码引坑?甚至,这么开 ...

  9. android截长屏实现,android 手机截取长屏实例代码

    最近项目遇到一个需求:把当前页面保存到手机相册.想了想 我还不会呢,就百度了下大神的足迹,踏着大神的足迹,一路向前.废话不说,记录下,后期学习. public class ScreenUtils { ...

最新文章

  1. idea怎么提交到dev分支_IDEA设置git提交分支
  2. SciPy和Numpy处理能力
  3. r语言教程w3c,R语言 数据库
  4. jzoj4485-[GDOI 2016 Day1]第一题 中学生数学题【数学】
  5. 水彩手绘中华美食PNG素材,提高海报设计效率!
  6. spgwr | R语言与地理加权回归(Ⅰ-1):线性地理加权回归
  7. 【分层图最短路】通信线路
  8. MATLAB 安装包
  9. chrome 独立安装包下载
  10. Braid,另类游戏
  11. 慕课网EMOS在线办公系统源码笔记1-6章
  12. 图书馆管理系统设计说明书
  13. Ubuntu 18.04 ——— ROVIO运行与EVO的评测与使用
  14. python倒数切片_python的切片操作
  15. 中央电大 c语言程序设计a 试题,中央电大开放本科计算机科学与技术专业C语言程序设计(A)试题_1007...
  16. Spring in Action:@Vaild 表单验证不起作用
  17. 第一次将项目push到gitlab
  18. CSDN 如何修改用户名(CSDN ID)?
  19. 什么是JWT?详细讲解
  20. Compose Multiplatform 实战:联机五子棋

热门文章

  1. 28份R语言文档沟通推文汇总
  2. Ubuntu 20 安装包下载(清华镜像)
  3. Polyworks脚本开发学习笔记(六)-比较运算、数学运算、逻辑运算及流程控制
  4. Polyworks脚本开发学习笔记(十)-互动式开发及出错控制
  5. 用Java编写的模仿电子钟系统
  6. U盘exFAT格式转NTFS
  7. 杭州市人社局携手法大大,加速推广电子劳动合同
  8. android手机可以换字体吗,安卓怎么换字体 安卓手机字体替换教程
  9. 【飞行器】基于matlab GUI四旋飞行器模型【含Matlab源码 2075期】
  10. 2021-07-02Leetcode236.二叉树的最近公共祖先