歡迎關注我的公眾號:前端偵探
在平時開發中,經常會碰到一些需要判斷高度的場景,比如當超過一定高度後,需要自動出現展開摺疊按鈕,如下
傳統的思路肯定是透過JS
去動態計算容器的高度,但這樣就涉及到載入時機的問題,獲取早了可能元素還沒渲染好,晚了又會有明顯的卡頓感,或者會引起頁面的閃爍。
那有沒有僅透過CSS
的方法呢?
當然也是有的!要實現上面這個例子的效果,需要解決以下幾個問題:
- 如何判斷不同的高度?
- 如何在不同的高度下展示隱藏點選按鈕?
- 如何點選切換?
花幾分鐘一起看看吧?
一、先思考一下佈局
明確來講,CSS
現在已經有相關方法可以判斷高度了,那就是CSS容器查詢。不過這個特性太高階了,目前幾乎還不能實戰,我們這次介紹一種更加傳統的方式。
如何判斷不同的高度?換句話來說,什麼樣的佈局在不同的高度下會有截然不同的效果?
思考一下
?
?
絕對定位?位置完全固定了,不行。
flex
佈局?好像也只能控制水平方向上
grid
佈局?這個水太深,沒來得及研究(可能也行?)
等等,除了以上,還有一個現在都避而不談的浮動佈局,為啥現在都很少用了呢,原因在於浮動佈局非常脆弱,細小的尺寸變化都能引起整個佈局的坍塌。我記得以前用浮動佈局的時候,都需要尺寸精確,稍微出一點差錯就導致浮動元素不知道跑哪去了...
既然對尺寸非常敏感,是不是和本文的臨界高度有一定聯絡呢?
沒錯,今天要用到的方式就是浮動佈局。
二、浮動佈局的奧妙
一步一步,來搭建我們所需要的頁面雛形。
我們先來看一個有趣的現象,這裡有一個容器,裡面有3
個子節點,分別為A
、B
、C
,其中A
左浮動,B
、C
右浮動
<div class="box">
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
</div>
.a{
width: 100px;
height: 100px;
float: left;
background-color: cadetblue;
}
.b{
width: 300px;
height: 100px;
float: right;
background-color: coral;
}
.c{
width: 50px;
height: 100px;
float: right;
background-color: darkgreen;
}
當橫向空間足夠時,效果是這樣的
此時,A
貼近左邊,B
貼近右邊,C
貼著B
如果橫向空間不足,那麼C
就會換行
現在C
看似好像跑到了B
的下方,其實是因為B
的高度還沒有超過A
當B
的高度超過A
時,那麼C
會有如下表現
此時,C
貼在B
的左側,A
的下方
是不是很神奇?除了浮動佈局,沒有什麼方法可以實現這樣的效果了吧。?那麼,這和本文的例子有什麼關係呢?
別急,其實這是一種極端情況,接著往下看
三、極端情況下的浮動表現
我們可以設定一個極限狀態,比如A
的高度充滿容器,B
的寬度充滿容器,此時B
肯定會掉下來,我們用負的margin
讓B
仍保持在一行,如下
.a{
width: 50px;
height: 100%;
}
.b{
width; 100%;
margin-left: -50px;
}
此時,B
的高度不高於A
,所以C
仍然是貼在B
的下方,並且靠右。現在讓B
的高度超過A
,也就是超出容器高度,就變成了這樣
此時,C
位於A
的下方。也就是,僅僅因為高度超過了一個臨界值,C
就得到了兩種截然不同的位置,如下
下面是動態演示(動態改變B的高度)
試想一下,把C
當做是“展開摺疊”按鈕,在這個基礎上挪動一下C
的相對位置,移到正下方,是不是就是我們需要的效果了呢?下面的虛線框示意移動後的位置,這樣在視區範圍內,虛線框在高度不足時就是不可見的,只有在超過固定高度後才可見,示意如下(觀察虛線框的位置)
完整 demo 可以檢視以下任意連結
整個原理就是這樣了,下面來看具體實現
四、CSS 具體實現
現在回到最開頭的例子,根據前面的 demo 原型,可以改造成以下結構
<div class="content">
<pre class="text">
很多內容...
很多內容...
</pre>
<label for="c1" class="btn"></label>
</div>
這裡.text
就相當於B
,.btn
就相當於C
,至於A
完全可以用偽元素::before
來代替
由於有點選切換的互動,所以需要用到input checkbox
,和label
關聯起來,所以結構最終改造成這樣
<div class="section">
<input class="content-check" type="checkbox" id="c1" hidden>
<div class="content">
<pre class="text">
很多內容...
很多內容...
</pre>
<label for="c1" class="btn"></label>
</div>
</div>
然後加點樣式美化一下吧,由於原理和前面是完全一致的,這裡就不重複展示具體細節了
.content{
width: 400px;
max-height: 200px;
overflow: hidden;
border-radius: 4px;
outline: 2px dashed royalblue;
}
.section{
display: flex;
}
pre{
white-space: pre-wrap;
}
.content::before{
content: '';
width: 100px;
height: 100%;
float: left;
}
.btn{
float: right;
width: 100px;
text-align: center;
position: relative;
left: calc(50% - 50px);
transform: translateY(-100%);
cursor: pointer;
}
.btn::after{
content: '';
display: block;
height: 34px;
background-color: #666;
transition: .2s background-color;
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E %3Cpath d='M143 352.3L7 216.3c-9.4-9.4-9.4-24.6 0-33.9l22.6-22.6c9.4-9.4 24.6-9.4 33.9 0l96.4 96.4 96.4-96.4c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9l-136 136c-9.2 9.4-24.4 9.4-33.8 0z'%3E%3C/path%3E %3C/svg%3E") center/ 24px 24px no-repeat;
}
.btn:hover::after{
background-color: royalblue;
}
.btn::before{
content: '';
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 34px;
}
.text{
box-sizing: border-box;
width: 100%;
padding: 10px 15px;
float: right;
line-height: 1.5;
margin: 0;
margin-left: -100px;
font-size: 18px;
color: #232323;
}
效果如下:
然後是點選切換效果,可以用:checked
來控制
.content-check:checked+.content{
max-height: fit-content;
}
.content-check:checked+.content .btn{
left: auto;
right: calc(50% - 50px);
}
.content-check:checked+.content .btn::after{
transform: scaleY(-1);
}
這樣就可以控制不同的狀態了
還可以加點遮罩,讓點選處有一種淡出弱化的效果,表示別急,下面還有內容
.text{
/* */
-webkit-mask: linear-gradient(red 150px, transparent 200px);
}
效果如下
完整 demo 可以訪問以下任意連結
五、最後總結一下
想不到浮動佈局還能實現這樣的功能,總的來說,這是一種成本低廉但需要點想象力的實現方式,適應性和相容性也都不錯,下面總結一下
- 佈局有很多種,浮動佈局比較特殊
- 浮動佈局非常脆弱,細小的尺寸都能引起整個佈局的坍塌
- 超過指定高度後,由於浮動佈局引起的坍塌,正好可以區分兩種情況
- 透過改變按鈕本身的相對位置,可以讓案例在超出指定高度後才可見
- 點選切換可以用
input:check
和label
相關聯實現 - 淡出弱化效果可以新增一層蒙版
mask
,表示下面還有內容
最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤
歡迎關注我的公眾號:前端偵探
本文參與了SegmentFault 思否寫作挑戰賽,歡迎正在閱讀的你也加入。