mlellipsis.js-實現多行文字溢出隱藏顯示省略號

超出容器部分文字做省略,這是基本寫前端代碼都會遇到的問題。

某些位置爲了表現完美不額外加滾動條,都會要求多出的部分作省略,例如文章簡介”今天天氣真的好…”,文本之後部分點擊進入詳情頁才能看到,超出多餘的用”…”省略。

然而,一般這些簡介都是用後臺語言去作處理,例如只顯示前140字等,如何用前端代碼實現?

你會想到用css的overflow:hidden;white-space:nowrap;text-overflow:ellipsis實現省略,然而這隻能省略一行的文字,如果多行文字則失效,應該如何解決?

在新版的chrome中有一個屬性-webkit-line-camp屬性,它允許你指定特定行數省略。

例如:3行後省略

1 text-overflow:ellipsis;
2  
3 display:-webkit-box;
4  
5 -webkit-line-clamp:3;
6  
7 -webkit-box-orient:vertical;

另外,在opera中也有相關的支持屬性text-overflow:-o-ellipsis-lastline,他能識別超出容器的最後一行作省略,然而明顯地,chrome的做法更優雅,更靈活。

可是,以上都是個別瀏覽器的實現,想要各種瀏覽器(包括IE哥哥)都用上,怎麼辦?

於是我就寫了這個東西:mlellipsis.js

先猛戳我這個簡陋的demo,調用方法其實很簡單:

1 element.mlellipsis(5);//element即dom元素

其中,5是行數,也就是和chrome的-webkit-line-clamp實現方式一樣,指定特定行數顯示;

若用jquery的朋友,可以在獲取元素後把它轉化爲js的dom對象。

例如:

1 var node = $(".demo > [")[0];
2  
3 node.mlellipsis(5);

具體做法如下:

1 獲取該調用node結點的文字大小、行高、文本的高度;

2 根據行高*想要省略的行數row,判斷文本的高度是否大於需要省略的高度,如果大於則不需要隱藏(例如字數一行就能顯示,那麼輸入row=2也只會顯示一行);

3 因爲會省略文字,所以先把完整文字內容寫入標籤的title屬性,hover後可以查看全部原文本;

4 動態截斷文本後面的文字,並加上”…”,直到截斷後的文字高度和目標的高度一樣,則不攔截;

逐步分析:

1.獲取計算後屬性

首先獲取node節點的三個值會涉及到瀏覽器的兼容性,例如一般現代瀏覽器可用新屬性getComputedStyle獲取計算後的css屬性,而可愛的IE6-8不支持這個屬性,需要用this.currentStyle。

「ps:具體差別可點擊這裏參看zxx對getComputedStyle介紹的文章」

其實如果用jquery的話可以直接調用css()方法獲取,然而懶得加載jquery,因此我重寫了一次。

01 Element.prototype.getFinalStyle=function(property){
02 var s;
03 if(window.getComputedStyle){
04 s = window.getComputedStyle(this, null)[property];
05 }
06 else{
07 s = this.currentStyle.property;
08 }
09 return s.substring(0,s.length-2);//減去元素的單位px
10 }

因此,獲取元素屬性的就很簡單了:

1 //獲取計算後的樣式
2  var fontSize = this.getFinalStyle("fontSize");
3  var lineHeight = this.getFinalStyle("lineHeight");
4  var height = this.getFinalStyle("height");

注意了,我重寫的這個只爲了適這個js,因此其他的屬性可能不支持,例如沒有px或者單位不爲px的屬性值。

//路人甲:把s.substring()不寫進去就行了嘛,那就可以兼容了

//tq:我就是懶……

然而,這裏我還發現了一個問題,就是line-height值,一般如果不設置line-height會是什麼值?1.5麼?

非也,會是獲得”normal”的一個string類型,我再找了一下資料{資料1,資料2}發現normal是個神奇的數值,不同瀏覽器、不同字體、不同系統都會有各自的解析!

//tq:omg…那怎麼辦

是的,我們要獲取正確的高度,就一定要獲得精確地line-height值。因此我看了各個line-height的大致比例,我決定把它reset掉,就是如果line-height爲normal,則我把line-height設爲1.5,即1.5倍的font-size;

2.獲取內容文本

無獨有偶,獲取文本的時候也會遇到瀏覽器兼容的問題,除了firefox外,其他瀏覽器都能支持innerText屬性,因此直接調用this.innerText木有問題,然而ff的innerText卻爲undefined,但它卻用textContent可以實現相同效果。具體差別點擊這裏

你懂的,我繼續重寫:

1 Element.prototype.getText=function(){
2 if(this.innerText){return this.innerText;}
3 else{return this.textContent;}
4 }
5  
6 Element.prototype.setText=function(str){
7 if(this.innerText!=""){this.innerText=str||"";}
8 else{this.textContent=str||"";}
9 }

調用方法就簡單地this.getText()即可或者this.setText(“str”)改變其值

3.設置title值

這個很簡單

1 var str = this.getText();
2  this.setAttribute("title",str);

4.關鍵步驟:動態截取文本

這裏用了正則,匹配當文本高度小於目標高度(省略行數x行高=元素省略時該顯示的目標高度)時,不斷截取後面的文本,並加上”…”省略號。

01 //減去末尾文字
02  while(dheight<this.clientHeight){
03 str = this.getText();
04 this.setText(str.replace(/(\s)*([a-zA-Z0-9]+|\W)(\.\.\.)?$/,"..."));
05  }
06  
07 /*
08  * /(\s)*([a-zA-Z0-9]+|\W)(\.\.\.)?$/ 正則解析:
09  * (\s)* 0或多個空白
10  * ([a-zA-Z0-9]+|\W) 一個或多個字母數字 或 任意不是字母,數字,漢字的字符
11  * (\.\.\.)? 零個或一個...
12  */

5.簡單剪枝

看似結束了,但是如果文本有8000+,但是只需要顯示前幾行,則只顯示100+字呢?

那會由於需要不斷截取字符,循環會瘋掉。用chrome的調試行數console.time()和console.endTime()測試後發現,如果不優化直接上的話8800字需要17s!

然而這裏只需要簡單剪枝,就可以保持無論多少文字都幾十ms的水平了。

剪枝思考:初步打算用二分法,就是當文本高度大於目標高度2倍,則截斷一半文字。然而,再考慮到文本里分爲三種:中文字、英文字、字符;一般來說,中文字=2英文字=2字符。因此最壞情況可能前面一半字符全部是英文,後面一半文本全部是中文,這樣二分法就會截取到25%的高度,因此考慮到最壞情況,需要3倍才截斷。

1 while(dheight*3<this.getFinalStyle("height")){
2 this.setText(str.substring(0,str.length/2));
3 str = this.getText();
4  }

這裏剪枝還可以大大優化,歡迎提出寶貴意見~*_*

以上,兼容到IE8+,chrome,firefox,safari,opera。

IE7以下不支持element.prototype的原型鏈擴展,果斷鄙視之。

下載:

mlellipsis.js(完整版帶註釋:1.70k)

mlellipsis.min.js(壓縮版:1.06k)

出處targetkiller 原文點此  http://targetkiller.net/mutiple-line-ellipsis/

發佈了119 篇原創文章 · 獲贊 142 · 訪問量 405萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章