<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何實(shí)現(xiàn)文本內(nèi)容折疊并顯示“...查看全部”?

          共 3236字,需瀏覽 7分鐘

           ·

          2020-11-25 02:53

          來源 |?https://wintc.top/article/58
          多行文本超過指定行數(shù)隱藏超出部分并顯示“...查看全部”是一個(gè)常遇到的需求,網(wǎng)上也有人實(shí)現(xiàn)過類似的功能,不過還是想自己寫寫看,于是就寫了一個(gè)vue的組件,本文簡單介紹一下實(shí)現(xiàn)思路。
          遇到這個(gè)需求的同學(xué)可以嘗試一下這個(gè)組件,支持npm安裝使用:
          組件地址:https://github.com/Lushenggang/vue-overflow-ellipsis
          在線體驗(yàn):https://wintc.top/laboratory/#/ellipsis

          一、需求描述

          長度不定的一段文字,最多顯示n行(比如3行),不超過n行正常顯示;超過n行則在最后一行尾部顯示“展開”或“查看全部”之類的按鈕,點(diǎn)擊按鈕則展開顯示全部內(nèi)容,或者跳轉(zhuǎn)到其它頁面展示所有內(nèi)容。
          預(yù)期效果如下:


          二、實(shí)現(xiàn)原理

          純css很難完美實(shí)現(xiàn)這個(gè)功能,所以還得借助js來實(shí)現(xiàn),實(shí)現(xiàn)思路大體相似,都是判斷內(nèi)容是否超過指定行數(shù),超過則截取字符串的前x個(gè)字符,然后然后和“...查看全部”拼接在一起,這里的x即截取長度,需要?jiǎng)討B(tài)計(jì)算。
          想通過上述方案實(shí)現(xiàn),有幾個(gè)問題需要解決:
          • 怎樣判斷文字是否超過指定行數(shù)

          • 如何計(jì)算字符串截取長度

          • 動(dòng)態(tài)響應(yīng),包括響應(yīng)頁面布局變動(dòng)、字符串變化、指定行數(shù)變化等

          下面具體研究一下這些問題。

          1、怎樣判斷一段文字是否超過指定行數(shù)?

          首先解決一個(gè)小問題:如何計(jì)算指定行數(shù)的高度?我首先想到的是使用textarea的rows屬性,指定行數(shù),然后計(jì)算textarea撐起的高度。
          另一個(gè)方法是將行高的計(jì)算值與行數(shù)相乘,即得到指定行數(shù)的高度,這個(gè)辦法我沒嘗試過,但是想必可行。
          解決了指定行數(shù)高度的問題,計(jì)算一段文字是否超過指定行數(shù)就很容易了。
          我們可以將指定行數(shù)的textarea使用絕對(duì)定位absolute脫離文檔流,放到文字的下方,然后通過文本容器的底部與textarea的底部相比較,如果文本容器的底部更靠下,說明超過指定行數(shù)。
          這個(gè)判斷可以通過getBoundingClientRect接口獲取到兩個(gè)容器的位置、大小信息,然后比較位置信息中的bottom屬性即可。
          可以這樣設(shè)計(jì)DOM結(jié)構(gòu):
          {{ showContent }} <-- showContent表示字符串截取部分 --> ... 查看更多
          然后使用css控制textarea,使其脫離文檔流并且不能被看到以及被觸發(fā)鼠標(biāo)事件等(textarea標(biāo)簽中的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、如何計(jì)算字符串截取長度x——雙邊逼近法(二分思想)

          只要可以判斷一段文字是否超過指定行數(shù),那我們就可以動(dòng)態(tài)地嘗試截取字符串,直到找到合適的截?cái)嚅L度x。這個(gè)長度滿足從x的位置截?cái)嘧址?,前半部?“...查看全部”等文字剛好不會(huì)超出指定行數(shù)N,但是多截取一個(gè)字,則會(huì)超出N行。
          最直觀的想法就是直接遍歷,讓x從0開始增長到顯示文本總長度,對(duì)于每個(gè)x值,都計(jì)算一次文字是否超過N行,沒超過則加繼續(xù)遍歷,超過則獲得了合適的長度x - 1,跳出循環(huán)。當(dāng)然也可以讓x從文本總長度遞減遍歷。
          不過這里最大的問題在于瀏覽器的回流和重繪。因?yàn)槲覀兠看谓厝∽址夹枰獮g覽器重新渲染出來才能得到是否超過N行,這過程中就觸發(fā)了瀏覽器的重繪或回流,每次循環(huán)都會(huì)觸發(fā)一次。
          而對(duì)于正常的需求來說,假設(shè)N取值是3,那很可能每次計(jì)算會(huì)導(dǎo)致50次以上的重繪或回流,這中間消耗的性能還是非常大的,不小心可能就是幾十毫秒甚至上百毫秒。
          這個(gè)計(jì)算過程應(yīng)該在一個(gè)任務(wù)(即常說的”宏任務(wù)“)中完成,否則計(jì)算過程中會(huì)出現(xiàn)顯示閃動(dòng)的”異常“情況,所以可以說計(jì)算過程是阻塞的,因此計(jì)算的總時(shí)間一定要控制到非常低,即要減少計(jì)算的次數(shù)。
          可以考慮使用"雙邊逼近法"(或稱”二分法“)查找合適的截取長度x,大大減少嘗試的次數(shù)。第一次先以文本長度為截取長度,計(jì)算是否超過N行,沒超過則停止計(jì)算;超過則取1/2長度進(jìn)行截取,如果此時(shí)沒超過N行,則在1/2長度到文本長度之間繼續(xù)二分查找,如果超過則在0到1/2文本長度中繼續(xù)二分查找。
          直到查找區(qū)間開始值與結(jié)束值相差為1,則開始值即為所求。具體實(shí)現(xiàn)可以看下文中的完整代碼。

          3、監(jiān)聽頁面變動(dòng)

          對(duì)于vue項(xiàng)目來說,傳入組件的字符串、行數(shù)等可能隨時(shí)改變,可以watch這些屬性變化,然后重新計(jì)算一次截取長度。
          另一方面,對(duì)于頁面布局而言,可能會(huì)因?yàn)槠渌撁嬖氐脑鰟h或者樣式改變,導(dǎo)致頁面布局變動(dòng),影響到文本容器的寬度,此時(shí)也應(yīng)該重新計(jì)算一次截取長度。
          監(jiān)聽文本容器寬度的變化,可以考慮使用ResizeObserver來監(jiān)聽,但是這個(gè)接口的兼容性不夠好(IE各個(gè)版本都不支持),因此選擇了一個(gè)npm庫element-resize-detector來監(jiān)測(cè)(非常好用)。

          三、代碼實(shí)現(xiàn)

          完整的代碼實(shí)現(xiàn)如下:

          在代碼實(shí)現(xiàn)中refresh函數(shù)用于計(jì)算截取長度,在文本內(nèi)容、rows屬性等發(fā)生改變或者文本容器尺寸改變時(shí)將被調(diào)用。
          每次refresh調(diào)用會(huì)異步地遞歸調(diào)用多次checkLoop,refresh可能重新調(diào)用,新的refresh調(diào)用將結(jié)束之前的checkLoop的調(diào)用。

          四、其它

          1、支持html串的考慮

          現(xiàn)在的實(shí)現(xiàn)方案并不支持內(nèi)容是html文本,如果需要支持HTML文本,問題將復(fù)雜許多。主要在于HTML字符串的解析和截?cái)?,不像文本字字符串那么簡單?/span>
          不過或許可以借助瀏覽器的Range API 來實(shí)現(xiàn)截?cái)辔恢玫亩ㄎ?,Range的insertNode以及setStart接口可以將“...查看全部”插入到指定位置,而如果插入位置剛好符合需要,則可以通過Range.cloneContents()")接口取得截取HTML字符串的相關(guān)內(nèi)容,理論上是可行的,不過具體細(xì)節(jié)以及處理效率得實(shí)踐后才知道。

          2、減少瀏覽器回流的影響

          上述實(shí)現(xiàn)方案中,每一次截取都需要瀏覽器重新渲染DOM,即重繪。重繪的影響還比較小,而如果截取的字符串行數(shù)發(fā)生改變,還會(huì)引發(fā)文本容器的高度變化,這時(shí)候就會(huì)導(dǎo)致瀏覽器回流,而文本容器在文檔流中,回流將會(huì)影響整個(gè)文檔。
          想解決這個(gè)問題,可以使用一個(gè)脫離文檔流的元素來進(jìn)行字符串動(dòng)態(tài)截?cái)嗪蟮匿秩九c判斷,布局就類似上述的textarea。
          因?yàn)椴辉谖臋n流中,回流的影響范圍就會(huì)減少到該元素自身。獲得截?cái)嚅L度后再截?cái)辔谋?,渲染到真正的文本容器即可?/span>
          本文僅作為一個(gè)簡單的原理概述的示例,沒有做這個(gè)處理,對(duì)具體細(xì)節(jié)感興趣的同學(xué),可以查看github倉庫代碼。

          瀏覽 15
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲日日久 | 成人网址一区二区三区 | 翔田千里A片一二三 | 久久精品视频6 | 一级特黄A大片 |