文章首發(fā)于我的 個(gè)人博客網(wǎng)站
雖然文本截?cái)嗖⒉皇鞘裁春軓?fù)雜的東西掷漱,但瀏覽器卻沒(méi)有較完善的原生支持质况。于是誕生了下面各種妖魔鬼怪的解決方案园蝠。每個(gè)方案都有各自的優(yōu)缺點(diǎn)和適用場(chǎng)景,請(qǐng)根據(jù)自己的需要選擇合適的方案厨幻。
單行截?cái)?/h3>
.single-line-truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.single-line-truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
white-space: nowrap;
white-space 用于指定空白符(white space)的行為诈火。 值 nowrap 會(huì)將多個(gè)連續(xù)空格符或換行符視為一個(gè)空格符。默認(rèn)情況下,文本超過(guò)容器寬度時(shí)溶浴,會(huì)自動(dòng)在合適的地方添加換行符進(jìn)行換行。設(shè)置了該值后管引,換行效果會(huì)被消除士败,使文本不進(jìn)行換行,從而實(shí)現(xiàn)文本單行顯示的效果。此時(shí)我們會(huì)發(fā)現(xiàn)文本發(fā)生了溢出:后面的文本內(nèi)容跑到容器外面去了谅将。
對(duì)此漾狼,我們?cè)偈褂?overflow: hidden
,將文本進(jìn)行截?cái)嗵幚砑⒈郏瑢⒊鋈萜黠@示的文本隱藏起來(lái)逊躁。如果希望在文本默認(rèn)處使用省略號(hào),使用 text-overflow: ellipsis;
來(lái)指定文本溢出的方式隅熙,為文本末追加合適的截?cái)嗖⑻砑邮÷蕴?hào) …
稽煤。
可以使用使用雙引號(hào)包裹的字符串值(如 text-overflow: "......"
)來(lái)自定義截?cái)嘧址^大多數(shù)瀏覽器都不支持(貌似只有 firefox 支持)囚戚,不推薦使用酵熙。
多行截?cái)啵?webkit-line-clamp
.multi-line {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
-webkit-line-clamp
可以指定文本在第幾行進(jìn)行截?cái)嗖⑻砑? …
。該屬性只有在 display 屬性設(shè)置成 -webkit-box 或者 -webkit-inline-box 并且 -webkit-box-orient 屬性設(shè)置成 vertical時(shí)才有效果驰坊。
隨著 Firefox(68 版本開(kāi)始) 的支持匾二,該方案可以說(shuō)是目前多行文本截?cái)嗟牟诲e(cuò)選擇,而且這種寫(xiě)法是官方提供的寫(xiě)法拳芙,而不是 CSS trick察藐。
不使用截?cái)嘧址ㄈ?…
)的多行截?cái)?/h3>
.multi-line {
line-height: 20px;
max-height: 60px;
overflow: hidden;
word-break: break-all;
}
.multi-line {
line-height: 20px;
max-height: 60px;
overflow: hidden;
word-break: break-all;
}
如果你不在意截?cái)嘧址厦孢@種寫(xiě)法就足夠了舟扎,它會(huì)最多保留三行文本分飞。word-break
屬性用于指定單詞的斷行行為。值 break-all
可以保證一個(gè)長(zhǎng)度超過(guò)容器寬度的英文單詞(連續(xù)的英文字符)能夠不溢出容器睹限,而是從中間截?cái)鄵Q行譬猫。一般來(lái)說(shuō)這個(gè)屬性是需要添加的,否則會(huì)因?yàn)橐粋€(gè)很長(zhǎng)的西歐單詞邦泄,導(dǎo)致部分內(nèi)容的缺失删窒。值 break-word
也有類似效果裂垦,但它會(huì)通過(guò)更早的換行來(lái)盡量保證英文單詞不會(huì)被從中間截?cái)唷?/p>
雖然一般情況來(lái)說(shuō)顺囊,不會(huì)出現(xiàn)一個(gè)英文單詞長(zhǎng)度超過(guò)容器寬度的情況,但測(cè)試并不這么認(rèn)為……
絕對(duì)定位法
其實(shí)就是通過(guò)絕對(duì)布局蕉拢,將容器中的一個(gè)子元素放到右下角特碳。我們?cè)谏弦粋€(gè)方案的基礎(chǔ)上做一些修改:
.multi-line {
position: relative;
line-height: 20px;
max-height: 60px;
overflow: hidden;
word-break: break-all;
}
.multi-line::before {
content: "…";
position: absolute;
top: 40px;
right: 0;
background-color: #fff;
}
偽元素 ::before(當(dāng)然也可以使用 ::after) 務(wù)必要設(shè)置和容器元素一樣的背景,否則會(huì)出現(xiàn)文字重疊的效果晕换。此外午乓,請(qǐng)注意我們使用的是 top: 40px
,而不是 bottom: 0
闸准。前者對(duì)應(yīng)的位置是第三行的頂部位置益愈,當(dāng)文本不超過(guò)兩行的時(shí)候,是不會(huì)出現(xiàn)該截?cái)嘧址模欢笳邉t不管文本有幾行都會(huì)顯示截?cái)嘧址羝洌@樣顯然不合適敏释。
但很明顯這種寫(xiě)法有一個(gè)致命的問(wèn)題:只要文本超過(guò)兩行就會(huì)顯示截?cái)嘧址词刮淖治茨芴畛錆M第三行摸袁。且可能會(huì)將字符從中間截?cái)啵梢酝ㄟ^(guò)設(shè)置透明度背景緩解)钥顽。
絕對(duì)定位法改良
前面提到的方案,缺點(diǎn)非常明顯靠汁,但是還是有辦法進(jìn)行改良的蜂大,但同樣會(huì)導(dǎo)致一些其他新的問(wèn)題。我們先看看 css:
.multi-line-absolute-sub-element-plus {
position: relative;
padding-right: 20px;
line-height: 20px;
max-height: 60px;
overflow: hidden;
word-break: break-all;
}
.multi-line-absolute-sub-element-plus::before {
content: "…";
position: absolute;
bottom: 0; /* 或 top: 40px; */
right: 0;
background-color: #fff;
}
.multi-line-absolute-sub-element-plus::after {
content: "";
position: absolute;
right: 0;
width: 20px;
height: 20px;
background-color: #fff;
}
相比上一種方法蝶怔,該方案通過(guò)設(shè)置了 padding-right: 20px;
來(lái)空出一段右邊距奶浦,用于放置截?cái)嘧址H缓蟪耸褂靡粋€(gè)放置截?cái)嘧址?::before 元素添谊,我們?cè)傺a(bǔ)充一個(gè) ::after 元素财喳,相比 ::before,該元素沒(méi)有指定 top 或 bottom斩狱,此時(shí)top 和 bottom 的值就會(huì)被瀏覽器設(shè)置為默認(rèn)的 auto耳高。對(duì)于 absolute 定位,此時(shí)的垂直距離就會(huì)為設(shè)為 當(dāng)前元素如果是 static 定位時(shí)的垂直距離所踊,其實(shí)就是文本內(nèi)容的最后一個(gè)字符后面泌枪。
這樣,::after 就能永遠(yuǎn)位于最后一行文字的右側(cè)秕岛。當(dāng)行數(shù)小于或等于最大行數(shù)時(shí)碌燕,::after 會(huì)遮擋住 ::before;當(dāng)行數(shù)超過(guò)最大行數(shù)時(shí)則無(wú)法遮擋继薛,從而顯示出截?cái)嘧址?/p>
這種方案解決了前一個(gè)方案的所有問(wèn)題修壕,但卻有另一個(gè)新的問(wèn)題:容器元素需要額外提供右邊距,并讓截?cái)嘧址坞x于文本之外遏考,顯得有點(diǎn)奇怪慈鸠。
淡出效果
可以看到,在文本上添加截?cái)嘧址ㄈ缡÷蕴?hào))總有各種各樣的問(wèn)題灌具,或許我們需要換一種思路青团,設(shè)計(jì)另外一種樣式來(lái)表示文本被截?cái)啵蔷褪堑鲂Ч?/p>
.fade-out{
position: relative;
line-height: 20px;
max-height: 60px;
overflow: hidden;
word-break: break-all;
}
.fade-out::after {
content: "";
position: absolute;
top: 40px;
right: 0;
width: 40%;
height: 20px;
background-image: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1) 70%);
}
淡出效果的好處是在文本超過(guò)第二行咖楣,但沒(méi)有充滿第三行時(shí)督笆,::after 和背景融合在一起,看起來(lái)就像不存在一樣诱贿。這點(diǎn)比上一種方案要好一些娃肿。
使用 float
.float {
--after-width: 30px;
max-height: 60px;
overflow: hidden;
}
.float .content {
float: right;
margin-left: calc(-1 * var(--after-width)); /* -30px */
width: 100%;
line-height: 20px;
overflow: hidden;
}
.float::before {
content: "";
float: left;
height: 60px;
width: var(--after-width); /* 30px */
background-color: blanchedalmond;
}
.float::after {
content: "…";
float: right;
width: var(--after-width); /* 30px */
height: 20px;
background-color: #f04;
/* 偏移操作 */
position: relative;
left: 100%; /* 1. 右移容器的寬度距離 */
transform: translate(-100%, -100%); /* 2. 偏移自身的寬高距離,此時(shí)就剛好位于最后一行的最右邊 */
}
這個(gè)方案很巧妙地利用了 float 的特性。
首先我們忽略掉 ::after 的 偏移操作 的樣式料扰,來(lái)看看 ::after 的位置隨文本高度的變化锨阿。
- 當(dāng)文本內(nèi)容高度小于或等于容器高度時(shí),::after 會(huì)出現(xiàn)在 文本的下方最右邊记罚;
- 當(dāng)文本內(nèi)容高度大于容器高度(即發(fā)生截?cái)啵r(shí)墅诡,::before 和文本內(nèi)容出現(xiàn)高度高度差,形成了左下方的凹陷桐智,::after 會(huì)出現(xiàn)這個(gè) 左下角末早。
當(dāng)位于左下角時(shí),我們只需要將 ::after 向右偏移 容器寬度-自身寬度
的距離说庭,再像上移動(dòng) 自身高度
的位置然磷,即可抵達(dá)文本容器的右下角。如果文本沒(méi)有溢出刊驴,應(yīng)用了偏移操作的 ::after 也會(huì)跑出文本容器姿搜,不會(huì)影響樣式效果。
該方案的缺點(diǎn)是 只對(duì)定高有效捆憎,因?yàn)?::before 無(wú)法設(shè)置 max-height舅柜,只能設(shè)置 height 導(dǎo)致容器被撐高為固定高度。如果嘗試改為 max-height躲惰,::before 的高度會(huì)因?yàn)闆](méi)有文本支撐致份,高度變?yōu)?0,導(dǎo)致 ::after 無(wú)法抵達(dá)正確的位置础拨。
上面這些都是純 CSS 的實(shí)現(xiàn)氮块,下面我們?cè)倏纯葱枰玫?js 的實(shí)現(xiàn)。
顯示 “查看更多” 按鈕
像 :hover 可以在鼠標(biāo)劃過(guò)元素時(shí)應(yīng)用指定的樣式诡宗,我們也希望有個(gè)名為 :truncated 的偽類能夠在文本截?cái)喟l(fā)生時(shí)應(yīng)用指定樣式滔蝉,然而并沒(méi)有。沒(méi)有辦法塔沃,我們只能使用 js 來(lái)檢測(cè)文本是否發(fā)生了截?cái)嗔恕?/p>
其實(shí)思路也很容易想出來(lái)蝠引,就是比較容器高度和實(shí)際文本占據(jù)高度,如果文本高度更大芳悲,說(shuō)明發(fā)生了截?cái)嗔⒅猓駝t沒(méi)有發(fā)生截?cái)唷?/p>
.read-more {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
word-break: break-all;
max-width: 600px;
}
const content = document.querySelector('.read-more')
const btn = document.querySelector('button')
function setReadmoreVisible() {
if (content.scrollHeight > content.clientHeight) {
btn.style.display = 'inline-block'
} else {
btn.style.display = 'none'
}
}
window.addEventListener('resize', function() {
setReadmoreVisible()
}, false)
window.onload = function() {
setReadmoreVisible()
}
缺點(diǎn)是每次容器發(fā)生變化時(shí)边坤,都要手動(dòng)檢測(cè)判斷高度決定是否顯示 “查看更多” 按鈕名扛。當(dāng)然如果使用 ResizeObserver 來(lái)檢測(cè)容器的改變的話,就不用對(duì)每次給可能導(dǎo)致容器變化的事件手動(dòng)地一個(gè)個(gè)添加相應(yīng)函數(shù)了茧痒,但 ResizeObserver 的實(shí)現(xiàn) 兼容性比較差肮韧。
此外可能會(huì)將字符從中間截?cái)啵瑯涌梢酝ㄟ^(guò)給 ::after 設(shè)置透明度漸變的背景緩解。
預(yù)設(shè)一個(gè)最大文本字符長(zhǎng)度
我們可以預(yù)設(shè)一個(gè) 加上截?cái)嘧址笈螅笾履芴顫M最大一行的的文本的字符數(shù)量長(zhǎng)度超燃。然后手動(dòng)使用 String.prototype.slice 方法對(duì)源文本進(jìn)行截?cái)啵⒃谄浜筇砑咏財(cái)嘧址辛臁s 代碼大致如下:
因?yàn)椴煌愋妥址ǔW謱挷煌馀遥摲桨笩o(wú)法保證能盡可能在恰當(dāng)?shù)奈恢媒財(cái)唷?duì)于一些不太嚴(yán)格的場(chǎng)景约素,可以使用這種方案届良。
DEMO
上面的實(shí)現(xiàn)我都寫(xiě)了在 codepen 上寫(xiě)了 demo。請(qǐng)點(diǎn)擊這里的 鏈接 查看和測(cè)試圣猎。
參考
- css-tricks - Multi-Line Truncation with Pure CSS
- css-tricks - Line Clampin’ (Truncating Multiple Line Text)
- Multiline truncated text with “show more” button (with just CSS)
- Pure CSS for multiline truncation with ellipsis
- 掘金 - 可能是最全的 “文本溢出截?cái)嗍÷浴?方案合集