CSS 文本截?cái)喾椒偨Y(jié)和原理分析

文章首發(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;
}

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;
}

如果你不在意截?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è)試圣猎。

參考


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末士葫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子送悔,更是在濱河造成了極大的恐慌慢显,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欠啤,死亡現(xiàn)場(chǎng)離奇詭異荚藻,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)洁段,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)鞋喇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人眉撵,你說(shuō)我怎么就攤上這事侦香。” “怎么了纽疟?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵罐韩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我污朽,道長(zhǎng)散吵,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任蟆肆,我火速辦了婚禮矾睦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘炎功。我一直安慰自己枚冗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布蛇损。 她就那樣靜靜地躺著赁温,像睡著了一般坛怪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上股囊,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天袜匿,我揣著相機(jī)與錄音,去河邊找鬼稚疹。 笑死居灯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的内狗。 我是一名探鬼主播穆壕,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼其屏!你這毒婦竟也來(lái)了喇勋?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤偎行,失蹤者是張志新(化名)和其女友劉穎川背,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蛤袒,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熄云,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妙真。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缴允。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖珍德,靈堂內(nèi)的尸體忽然破棺而出练般,到底是詐尸還是另有隱情,我是刑警寧澤锈候,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布薄料,位于F島的核電站,受9級(jí)特大地震影響泵琳,放射性物質(zhì)發(fā)生泄漏摄职。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一获列、第九天 我趴在偏房一處隱蔽的房頂上張望谷市。 院中可真熱鬧,春花似錦击孩、人聲如沸迫悠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)及皂。三九已至,卻和暖如春且改,著一層夾襖步出監(jiān)牢的瞬間验烧,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工又跛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碍拆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓慨蓝,卻偏偏與公主長(zhǎng)得像感混,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子礼烈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容