超出容器部分文字做省略,這是基本寫前端代碼都會遇到的問題。
某些位置爲了表現完美不額外加滾動條,都會要求多出的部分作省略,例如文章簡介”今天天氣真的好…”,文本之後部分點擊進入詳情頁才能看到,超出多餘的用”…”省略。
然而,一般這些簡介都是用後臺語言去作處理,例如只顯示前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/