省略号,作为一种常见的文本处理方式,在很多情况下都十分常见。特别是当我们需要在省略号后面添加额外文字时,这种需求更是不少见。
然而,仅仅依赖 CSS 来实现兼容性、流畅的文本省略效果是不现实的。就拿 Vant4 的 TextEllipsis 文本省略功能来说,我们可以从中学到不少。
参考Vant4:TextEllipsis 文本省略,为大家分析一波案例(感觉麻烦可以去看下面的附带代码)。
在整个代码中,关键之处在于使用了 tail、middleTail 等关键手段。我们着重讨论 tail,即在文本尾部插入内容的操作。
简单的CSS隐藏
大家或多或少都曾编写过 CSS 省略文本的代码。比如,要将文本限制在四行内显示,可以使用以下 CSS 代码:
隐藏为四行
1 2 3 4 | overflow : hidden ; display : - webkit-box ; -webkit-box-orient: vertical; -webkit-line-clamp: 4 ; /* 控制显示行数 */ |
其中的限制
尽管如此,这种方法有着一定的局限性:
- 如何在省略号后添加展开、收起等文字?
- 添加后,如何保证文字与省略号在同一行?
- 展开后,如何保证文字在合适位置显示?
纯 CSS 可以实现这些效果,但实际操作起来会比较困难。
既然 CSS 不够理想,我们可以考虑使用 JavaScript 来实现这些功能。
首先,让我们来分析一波:
- 收起时保留的高度是多少?
- 如何截取超出高度的文字内容?
- 如何确保截取后的文字内容不影响原来的渲染和展示?
完成以上三点,我们就完成了任务。
开始分析
1 2 3 | < div class = "root" > 散文是一种文学形式,与之相对的称为韵文或诗文。散文可分为广义与狭义的解释,其中,广义散文最容易辨识与定义的方式,是“松散”的结构。也就是说,扣除其它文学形式重叠部分,运用普通语法结构,不讲究音韵,不讲究排比,没有任何束缚及限制的文字梳理方式,都可称为散文。除此,狭义散文是单指文学范畴内,结构松散之非韵文作品。文学专指的散文,历代作品有着各时代不同流变的脉络,而正因为松散带来的自由,散文作品表达出的思想通常有着丰富与圆满的特色 </ div > |
1 2 3 4 5 | .root { line-height : 1.6 ; overflow-wrap : break-word; word-break : break- all ; } |
首先解决:收起时要保留的高度是多少?
假设我们保留4行:
需要给予文本限高 : line-height: 1.6;
1 2 3 4 | row = 4 const maxHeight = Math.ceil( (Number(rows) + 0.5) * pxToNum(lineHeight) + "一些其他的容器高度:比如padding" ); |
在这个计算中,0.5 是一个调整值,用于在计算最大高度时考虑到文本行数的不确定性。这个调整值的目的是为了确保即使行数计算稍微超过了预期的行数,也能够提前截断文本,以防止文本超出容器的高度限制。
得到了高度,如何切割文字
注意看:tail()
函数是一个递归函数,用于在给定的容器高度限制下,计算文本的截断位置。让我来详细解释一下 tail()
函数的工作原理:
1 2 3 4 5 6 7 8 9 | const tail = (left, right) => { if (right - left maxHeight) { return tail(left, middle); } return tail(middle, right); }; container.innerText = tail(0, end); |
当递归结束时,函数会返回最终的截断位置,即最佳的截断位置。
递归都有自己结束判断,这个递归的结束点就在right - left ,
计算中间位置 middle
,并将文本内容从左边界到中间位置加上省略符号,然后设置到容器元素中
检查容器元素的高度是否超过了指定的最大高度限制。如果超过了,说明当前截断位置过早,需要继续向左边界靠近进行截断;否则,说明当前截断位置过晚,需要继续向右边界靠近进行截断。
最终找到,合适的middle值,在一起返回 content.slice(0, left) + dots
作为结点的 innerText
文字。
期间,一直在收缩,就算高度相同也在收缩右边界的内容, right - left 的作用就是刚刚好实现的截断位置以满足给定的最大高度限制,不会超出限制。
总结:这个 tail()
函数使用递归的方式,根据给定的容器高度限制,计算出文本的截断位置。递归的结束判断在 right - left ,通过不断调整
middle
的值,最终找到合适的截断位置以满足给定的最大高度限制,不会超出限制。
在切割下,保证文字内容不影响原来的渲染展示
为了不影响原文本的渲染展示,我们使用了克隆节点的方式。这里参考了 Vant 中的代码,并做了适当修改。
修改成了,更加适合JS宝宝体质的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | const rootNode = document.querySelector( ".root" ); 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; }; |
解析:window.getComputedStyle
是一个用于获取指定元素的所有计算样式的方法。它返回一个 CSSStyleDeclaration
对象,其中包含了指定元素的所有计算样式属性及其对应的值。
实现效果
实例代码
html
1 2 3 4 5 6 7 8 9 | .root { padding: 12px; line-height: 1.6; overflow-wrap: break-word; word-break: break-all; } < div class = "root" > 散文是一种文学形式,与之相对的称为韵文或诗文。散文可分为广义与狭义的解释,其中,广义散文最容易辨识与定义的方式,是“松散”的结构。也就是说,扣除其它文学形式重叠部分,运用普通语法结构,不讲究音韵,不讲究排比,没有任何束缚及限制的文字梳理方式,都可称为散文。除此,狭义散文是单指文学范畴内,结构松散之非韵文作品。文学专指的散文,历代作品有着各时代不同流变的脉络,而正因为松散带来的自由,散文作品表达出的思想通常有着丰富与圆满的特色 </ div > |
JS代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | 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 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 { 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文本展开收起的资料请关注IT俱乐部其它相关文章!