細說長談文字超長展開收起功能

若是隻是單純的文字超長,多餘文字...隱藏,那麼問題仍是比較簡單,幾行css代碼就搞定了,以下:css

overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
複製代碼

當前要實現的是文字超長,頁面在末尾顯示「展開」和「收起」兩中狀態,點擊能作響應動做,展開按鈕狀態出現的前提是要內容三行放不下了,才能出現展開按鈕,不然不該該出現展開按鈕。以下:
收起的樣式:
html

展開

展開的樣式:
web

收起

若是用上述的css來實現的,沒辦法捕捉到...的事件,沒有辦法再展開內容了。 接下來咱們用css和js來實現這兩種方式。
以前咱們嘗試過本身計算文字是否超長來作展開和隱藏,可是問題很多。dom

  • 頭部有標籤,得加入計算
  • 內容有換行標籤
  • 單行可顯示的字符數不是恰好對應上,如能夠顯示3個字符,那麼「aa中",則中字換行。
  • ……

總而言之通過複雜的代碼處理以後(計算標籤寬度,處理換行等),也基本能達到要求,可是總差點意思,「展開」按鈕不必定總在最右邊,效果不是太好。接下來介紹下很穩的效果。ide

js的實現

咱們首先用js來實現展開收起功能。字體

getBoundingClientRect

返回元素的大小及其相對於視口的位置。 返回值是一個 DOMRect 對象,這個對象是由該元素的 getClientRects() 方法返回的一組矩形的集合, 即:是與該元素相關的CSS 邊框集合 。 DOMRect 對象包含了一組用於描述邊框的只讀屬性——left、top、right和bottom,單位爲像素。除了 width 和 height 外的屬性都是相對於視口的左上角位置而言的。 優化

alt

同上,咱們執行代碼:ui

document.querySelector(".txt").getBoundingClientRect();
複製代碼

alt

瞭解 getClientRects

返回一個指向客戶端中每個盒子的邊界矩形的矩形集合。 返回值是ClientRect對象集合,該對象是與該元素相關的CSS邊框。每一個ClientRect對象包含一組描述該邊框的只讀屬性——left、top、right和bottom,單位爲像素,這些屬性值是相對於視口的top-left的。即便當表格的標題在表格的邊框外面,該標題仍會被計算在內。spa

實戰 getClientRects

var rectCollection = Element.getClientRects();
複製代碼

例子:3d

<div id="txt">我是一個小文本,<br>我是一個小文本</div>
複製代碼

執行代碼:

document.querySelector(".txt").getClientRects();
複製代碼

返回結果以下:

getClientRects
若是咱們將html的div改爲span

<span id="txt">我是一個小文本,<br>我是一個小文本</span>
複製代碼

返回結果以下:

span

對於行內元素,元素內部的每一行都會有一個邊框;對於塊級元素,若是裏面沒有其餘元素,一整塊元素只有一個邊框. 因此是div就返回了三個DOMRect,可是div只返回一個。

文字超長處理(一)

咱們採用文本遮住的方式來實現顯示展開和收起。

在顯示的文本外增長一個文本框

<div class="wrap">
  <span class="txt">我是一個小文本,<br>我是一個小文本</span>
  <span class="dot open">...展開</span>
</div>
複製代碼

而後咱們設定字體大小和行高。好比字體是18,行高1.5。假如顯示三行,則咱們設置外層最大爲183+92 = 72px; 若是是要動態文字大小,則同比計算便可。

.wrap{
  width:100%;
  max-height:72px;
  position:relative;
  line-height:1.5;
  font-size:18px;
}
.dot{
    position: absolute;
    right: 0;
    bottom: 0;
    background: rgba(255,255,255,.9);
}

複製代碼

這個時候若是是文字超長,咱們已經看到效果了。好比:

展開

接下來咱們須要把文字沒有超長的時候,隱藏展開按鈕便可。

第一步:判斷外層的高度。

document.querySelector(".wrap").getClientRects();

bottom: 258.359375
height: 72
left: 55
right: 399
top: 186.359375
width: 344
x: 55
y: 186.359375
複製代碼

若是height小於72,則直接隱藏,說明文字沒有超長。

第二步:獲取內部文字高度。

若是外層已經達到72px了,那麼可能超長,也多是恰好三行。

document.querySelector(".txt").getClientRects();
複製代碼

獲得以下數據。

txt

  • 若是行數大於三行,則是須要顯示展開按鈕。
  • 若是小於等於三行,則不須要顯示展開按鈕

這裏重點要看下,獲取getClientRects的行數。要去掉換行等的空行,即Bottom值同樣。

function getRectsLength(rects) {
    var line = 0, lastBottom=0;
    for(var i=0,len=list.length;i<len;i++){
        if(list[i].bottom ==lastBottom){
            return;
        }
        lastBottom = list[i].bottom;
    		line++; 
		}
    return line;
}
複製代碼

上面的處理方式,使用外層的wrap,須要是一個塊狀元素。若是外層不是塊狀元素,好比以下,簽名須要插入一個標籤,文字挨着標籤顯示。則只能用第二種方式實現。

alt

文字超長處理(二)

使用getClientRects方法來處理文字超長,因爲這個方法是實時返回頁面元素的邊界盒子,因此採用的是過後處理機制。

html頁面內容,在每個item裏面,存儲showTxt和hideTxt,開始內容都在showTxt裏面。

<span class="quan_feed_desc">
<span class="show_txt"></span>
<span class="dot">...</span>
<span class="hide_txt" style="display:none"></span>
<span class="quan_feed_more dot">展開</span>
</span>
複製代碼

開始渲染到頁面的時候是整個內容都渲染上去,即渲染到上面的show_txt,而後再針對頁面dom節點處理。當一頁數據渲染完成以後,再執行handleText。
獲取沒有處理過的列表項。

function handleText() {
    const dom = document.querySelectorAll(".quan_feed_desc:not([data-loaded]");
    if(dom.length==0) return;
    Array.from(dom).map((d)=>{
        handleDomLength(d);
    }); 
}
複製代碼

獲取getClientRects的行數。要去掉換行等的空行,即Bottom值同樣。

function getRectsLength(rects) {
    var line = 0, lastBottom=0;
    for(var i=0,len=list.length;i<len;i++){
        if(list[i].bottom ==lastBottom){
            return;
        }
        lastBottom = list[i].bottom;
    		line++; 
		}
    return line;
}
複製代碼

單獨處理某一項的行數。其中feedList是渲染的頁面數據。循環處理字符,若是行數超過3行,則一直減小顯示的文本數據,知道只有三行爲止。這裏有個優化點,加速回退的進程,減小回退次數,好比粗略計算文字的長度是否大幅高於3行,而後大幅回退文字。

function handleDomLength(dom) {
    const item = feedList[dom.dataset.index];
    const rects = dom.getClientRects();
    var h = getRectsLength(rects);
    if(h<3||!item.showTxt){
        dom.dataset.loaded = 1;
        continue;
    }
    
    dom.querySelectorAll(".dot").forEach(function (el) {
        el.style.display="inline-block";//把點點和展開放開,爲後面計算更真實
    });
    let t = item.showTxt;
    let showtxt = dom.querySelector(".show_txt");
    while(h>3&&t){
        let step=1,m;
        if(t.indexOf("<br/>")==0){//回退的時候,若是碰到換行要總體替換
            step = 5;
        }else if(t.indexOf("<br>")==0){
            step = 4;
        }else if(m = t.match(/\[[^\]]+\]/)){// [大哭]是表情,統一回退
            step = m[0].length;
        }
        t = t.slice(0,-step);
        showtxt.innerHTML = t;
        h = getLength(dom.getRectsLength());
    }
    item.hideTxt =item.showTxt.slice(0,t.length);
    item.showTxt = t;
    dom.dataset.loaded = 1;
}
複製代碼

這個處理方式,最大的好處就是很精準,基本上完美達到產品的需求,不太足的地方就是須要回退字符串,頻繁操做dom文本。

css的實現

css使用float來實現,以下:

alt

有個三個盒子 div,粉色盒子左浮動,淺藍色盒子和黃色盒子右浮動。
當淺藍色盒子的高度低於粉色盒子,黃色盒子仍會處於淺藍色盒子右下方。
若是淺藍色盒子文本過多,高度超過了粉色盒子,則黃色盒子不會停留在右下方,而是掉到了粉色盒子下。
使用float浮動原理的表現形式巧妙的展示這個功能。

<div class="wrap">
    <div class="text">
        我是中國人我是中國人我是中國人我是中國人我是中國人我是中國人我是中國人我是中國人我是中國人我是中國人我是中國人我是中國人我是中國人我是中國人
    </div>
</div>
複製代碼

這裏只有一個文本div,而後使用了before和after來實現,動態的出現「...更多」。

.wrap{
    height: 40px;
    line-height: 20px;
    overflow: hidden;
}
.wrap .text{
    float:right;
    margin-left: -5px;
    width: 100%;
    word-break: break-all;
}
.wrap::before{
    float:left;
    width: 5px;
    content:'';
    height: 40px;;
}
.wrap::after{
    float: right;
    height: 20px;
    line-height: 20px;
    width:4em;
    content:'...更多';
    margin-left:-4em;
    position: relative;
    text-align: right;
    left:100%;
    top:-20px;
    padding-right: 5px;
    background:-webkit-gradient(linear,left top,right top,from(rgba(255,255,255,.7)),to(white))
}
複製代碼

注意這裏須要指定line-height,代碼中而後指定after的top爲向上移動一行。

相關文章
相關標籤/搜索