純 CSS 檢測文本是否溢出
介紹一個CSS實用技巧
一直以來,CSS 都無法很好的檢測出一段文本是否溢出。但這又是一個非常普遍的需求,比如多行文本展開,展開按鈕只有在文本溢出的時候才出現(xiàn)。
時代在進步,CSS也在不斷推出新特性,現(xiàn)在,CSS終于可以完美的解決這個問題了,也就是可以準(zhǔn)確無誤地判斷文本是否溢出了,一起看看吧。
一、CSS 滾動驅(qū)動動畫
要實現(xiàn)文本溢出檢測,需要用到兩個新特性。
- CSS 滾動驅(qū)動動畫
- CSS 樣式查詢
為什么是這兩個呢?聽我慢慢分析。
首先我們想一想,在 JS中是如何判斷是否溢出的?很簡單。
dom.scrollHeight > dom.offsetHeight;
其實也就是表示這個容器是“可滾動”的,因為滾動高度超過了可視高度。
回到 CSS 這里,有沒有辦法區(qū)分呢?答案就是CSS滾動驅(qū)動動畫。
假設(shè)有這樣一個布局,就兩段文本。
<div class="txt">
歡迎關(guān)注前端偵探,這里有一些有趣的、你可能不知道的HTML、CSS、JS小技巧技巧。歡迎關(guān)注前端偵探,這里有一些有趣的、你可能不知道的HTML、CSS、JS小技巧技巧。歡迎關(guān)注前端偵探,這里有一些有趣的、你可能不知道的HTML、CSS、JS小技巧技巧。
</div>
<div class="txt">
歡迎關(guān)注前端偵探
</div>
稍微修飾一下,給個高度,讓文本可以超出滾動。
.txt{
height: 4em;
padding: 8px;
outline: 1px dashed #9747FF;
font-family: cursive;
border-radius: 4px;
}
效果如下:
左邊是可以滾動的,右邊是不能滾動的。
現(xiàn)在,我們給左邊加一個滾動驅(qū)動動畫,在滾動時慢慢改變文本的顏色。
.txt{
animation: check 1s;
animation-timeline: scroll(self);
}
@keyframes check{
to {
color: #9747FF;
}
}
注意這個scroll(self),self表示監(jiān)聽自身滾動,默認(rèn)是最近的祖先滾動容器,效果是這樣的。
可以看到隨著滾動,左邊文本的顏色也慢慢變化了。
接著激進一點,我們在動畫中把起始點都設(shè)置成一樣,這樣還沒開始滾動就自動變色了。
@keyframes check{
from,to {
/*動畫起始點設(shè)置成相同*/
color: #9747FF;
}
}
效果如下:
這樣即使還沒開始滾動,也能提前知道是否可滾動了。
然后,我們可以設(shè)置超出隱藏,也就是讓滾動容器“不能滾動”。
.txt{
overflow: hidden;
}
效果如下:
也就是說這種情況下,CSS滾動驅(qū)動動畫仍然可以被觸發(fā)。嘗試了一下,只要不是overflow:visible,CSS都認(rèn)為是“可滾動”的,即“溢出”狀態(tài)。
最后,我們將文本設(shè)置成超出顯示省略號。
.txt{
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
效果如下:
是不是有點能區(qū)分文本是否溢出了?至少目前從文本顏色可以很好判斷。
當(dāng)然,僅僅這樣是不夠,還需要更加自由,比如在超出時可以控制其他標(biāo)簽的狀態(tài),這就需要用到 CSS 樣式查詢了。
二、CSS 樣式查詢
下面介紹一下CSS樣式查詢。
@container - CSS: Cascading Style Sheets | MDN (mozilla.org)[1]
CSS 樣式查詢是容器查詢的一部分,從名稱也可以看出,它可以查詢元素的樣式,進而設(shè)置額外的樣式。
比如,我們要查詢顏色為紅色的容器,然后給子元素設(shè)置背景色為黑色,可以這樣。
<style>
div{
color:red;
}
@container style(color: red) {
p {
background: black;
}
}
</style>
<div>
<p>
</p>
</div>
有人可能會有疑問,為啥要設(shè)置子元素,直接設(shè)置本身不好嗎?其實是為了避免沖突,假設(shè)查詢到了color:red,然后你又設(shè)置了color:yellow,那瀏覽器該如何渲染呢?有點死循環(huán)了。所以為了避免這種情況,所有容器查詢都只能設(shè)置子元素樣式。
不過這種寫法目前還不支持,僅支持CSS變量的寫法,類似于這樣。
<style>
div{
--color:red;
}
@container style(--color: red) {
p {
background: black;
}
}
</style>
<div>
<p>
</p>
</div>
回到前面的例子,我們可以給文本加一個CSS變量,就叫做 --trunc吧,表示截斷。
.txt{
--trunc: false;
}
然后在滾動驅(qū)動動畫中改變這個變量。
@keyframes check{
from,to {
/*動畫起始點設(shè)置成相同*/
color: #9747FF;
--trunc: true;
}
}
這樣一來,滾動驅(qū)動動畫執(zhí)行的時候,這個變量也被賦值了。
最后我們就可以查詢這個樣式,給子元素設(shè)置樣式了,這里我們就用偽元素代替。
@container style(--trunc: true) {
.txt::after{
content: '';
position: absolute;
inset: 2px;
border: 1px solid red;
}
}
這段代碼表示當(dāng)查詢到--trunc: true的條件時,設(shè)置相應(yīng)的樣式,這里是畫了一個紅色的邊框,效果如下:
是不是非常容易?
你也可以查看以下在線鏈接(注意兼容性,需要 Chrome 115+,以下相同):
- CSS animation-timeline + @ container style (codepen.io)[2]
- CSS animation-timeline + @ container style (juejin.cn)[3]
有了這個作為區(qū)分,可做的事情就比較多了,下面來看幾個例子。
三、CSS 多行文本展開收起
這已經(jīng)是第四次用不同方式來實現(xiàn)這個效果了,前幾次的實現(xiàn)可以參考文章開頭部分。
這次來看新的實現(xiàn)方式。
首先還是把之前的結(jié)構(gòu)拿過來,這些結(jié)構(gòu)是為了實現(xiàn)右下角的“展開”按鈕必不可少的。
<div class="text-wrap">
<div class="text-content">
<label class="expand"><input type="checkbox" hidden></label>
歡迎關(guān)注前端偵探,這里有一些有趣的、你可能不知道的HTML、CSS、JS小技巧技巧。
</div>
</div>
相關(guān) CSS 如下:
.text-wrap{
display: flex;
position: relative;
width: 300px;
padding: 8px;
outline: 1px dashed #9747FF;
border-radius: 4px;
line-height: 1.5;
text-align: justify;
font-family: cursive;
}
.expand{
font-size: 80%;
padding: .2em .5em;
background-color: #9747FF;
color: #fff;
border-radius: 4px;
cursor: pointer;
float: right;
clear: both;
}
.expand::after{
content: '展開';
}
.text-content{
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
.text-content::before{
content: '';
float: right;
height: calc(100% - 24px);
}
.text-wrap:has(:checked) .text-content{
-webkit-line-clamp: 999;
}
.text-wrap:has(:checked) .expand::after{
content: '收起';
}
這時的效果是這樣的。
通過上一節(jié)的原理,我們通過滾動驅(qū)動動畫來判斷是否溢出,并使用CSS變量作為標(biāo)識,然后利用樣式查詢來控制展開按鈕的顯示狀態(tài),關(guān)鍵實現(xiàn)如下:
.expand{
/**/
display: none;
}
.text-content{
--trunc: false;
animation: check 1s;
animation-timeline: scroll(self);
}
@keyframes check{
from,to {
--trunc: true;
}
}
@container style(--trunc: true) {
.expand{
display: initial;
}
}
展開按鈕默認(rèn)是隱藏的,這樣只有在文本溢出的時候才出現(xiàn),效果如下:
效果出來了,不過在點擊展開后按鈕也跟著消失了。這是因為展開后,CSS檢測出這時沒有溢出,所以樣式查詢里的語句就不生效了,自然也就回到了之前的隱藏狀態(tài)。
要解決這個問題也很簡單,在展開的時候始終顯示按鈕就行了,用:checked可以判斷是否展開。
.text-wrap:has(:checked) .expand{
display: initial;
}
這樣就正常了,完美!
CSS方式的好處是監(jiān)控是實時的,比如手動改變?nèi)萜鞯膶挾龋矔詣语@示或者隱藏這個按鈕。
完整demo可以查看以下在線鏈接( Chrome 115+):
- CSS container style expand (codepen.io)[5]
- CSS container style expand (juejin.cn)[6]
四、CSS 文本超出時顯示 tooltips
還有一個比較常見的需求,就是希望在文本出現(xiàn)省略號時,鼠標(biāo)hover有tooltips提示,就像這樣。
原理和上面幾乎一致,我們一步步來看。
首先還是結(jié)構(gòu),沒什么特別的。
<div class="txt" data-title="這是一段可以自動出現(xiàn)tooltip的文本">
這是一段可以自動出現(xiàn)tooltip的文本
</div>
這里加了一個data-title,是用來顯示氣泡的,通過偽元素content獲取屬性內(nèi)容。
.txt{
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 8px;
outline: 1px dashed #9747FF;
font-family: cursive;
border-radius: 4px;
}
.txt::after{
content: attr(data-title);
position: absolute;
top: 0;
width: fit-content;
left: 50%;
margin: auto;
transform: translate(-50%,-100%);
background-color: rgba(0,0,0,.6);
padding: .3em 1em;
border-radius: 4px;
color: #fff;
opacity: 0;
visibility: hidden;
transition: .2s .1s;
}
效果如下:
目前是沒有任何提示的。
下面加上CSS溢出檢測,在檢測到溢出時hover生效。仍然是相同的代碼,添加一個滾動驅(qū)動動畫,然后樣式查詢。
.txt{
--trunc: false;
animation: check 1s;
animation-timeline: scroll(x self);
}
@keyframes check{
from,to {
--trunc: true;
}
}
@container style(--trunc: true) {
.txt:hover::after{
opacity: 1;
visibility: visible;
}
}
注意,這里的scroll(x self),加了一個x,因為這時的文本是橫向溢出的,所以需要加上滾動驅(qū)動軸(默認(rèn)是垂直方向)。
另外,由于超出隱藏,所以tooltip需要一個新的父級,不然就被裁掉了。
<div class="wrap">
<div class="txt" data-title="這是一段可以自動出現(xiàn)tooltip的文本">
這是一段可以自動出現(xiàn)tooltip的文本
</div>
</div>
.wrap{
position: relative;
}
這樣就能實現(xiàn)文本超出時顯示 tooltips。
完整demo可以查看以下在線鏈接( Chrome 115+):
- CSS container style tooltip (codepen.io)[7]
- CSS container style tooltip (juejin.cn)[8]
五、最后總結(jié)一下
CSS 就是這么神奇,將兩個幾乎不相關(guān)的特性組合起來,就能實現(xiàn)完全不一樣的功能,這可是在其他語言中做不到的,簡單回顧一下CSS檢測代碼。
.content{
--trunc: false;
animation: check 1s;
animation-timeline: scroll(x self); /*注意溢出方向*/
}
@keyframes check{
from,to {
--trunc: true; /*滾動驅(qū)動動畫*/
}
}
/*查詢溢出狀態(tài)*/
@container style(--trunc: true) {
}
是不是非常容易,幾乎是無侵入式的,下面總結(jié)一下本文重點。
- 要實現(xiàn)文本溢出檢測,需要用到兩個新特性,CSS滾動驅(qū)動動畫和CSS樣式查詢。
- CSS滾動驅(qū)動動畫可以檢測出容器是否可滾動,也就是溢出,即使是在超出隱藏的情況下。
- CSS樣式查詢可以查詢到CSS變量的變化,從而設(shè)置不同的樣式。
- 借助CSS滾動驅(qū)動動畫和CSS樣式查詢,可以很輕松的實現(xiàn)文本溢出檢測。
- 兩個實例:CSS多行文本展開收起和CSS文本超出時顯示 tooltips。
當(dāng)然除了以上一些案例,還可以做的事情很多,比如以前有寫一篇判斷指定高度后就顯示折疊按鈕,也可以用這種方式來實現(xiàn),幾乎所有與溢出相關(guān)的交互都可以純CSS完成。
至于兼容性,目前僅支持 chrome 115+,還是需要多等等,多多關(guān)注,說不定哪一天就能用上了呢,比如5年前推出的CSS scroll snap,現(xiàn)在幾乎可以愉快使用了,再也無需swiper.js這樣的庫了。
[1]@container - CSS: Cascading Style Sheets | MDN (mozilla.org): https://developer.mozilla.org/en-US/docs/Web/CSS/@container。
[2]CSS animation-timeline + @ container style (codepen.io): https://codepen.io/xboxyan/pen/jORrXBe。
[3]CSS animation-timeline + @ container style (juejin.cn): https://code.juejin.cn/pen/7346120235966267427。
[4]CSS 實現(xiàn)多行文本“展開收起”: https://juejin.cn/post/6963904955262435336。
[5]CSS container style expand (codepen.io): https://codepen.io/xboxyan/pen/qBwaaWW。
[6]CSS container style expand (juejin.cn): https://code.juejin.cn/pen/7346120018578374694。
[7]CSS container style tooltip (codepen.io): https://codepen.io/xboxyan/pen/oNOzzYb。
[8]CSS container style tooltip (juejin.cn): https://code.juejin.cn/pen/7346125496281333814。