CSS 實現超過固定高度後出現展開摺疊按鈕

XboxYan發表於2023-02-20
歡迎關注我的公眾號:前端偵探

在平時開發中,經常會碰到一些需要判斷高度的場景,比如當超過一定高度後,需要自動出現展開摺疊按鈕,如下

Kapture 2023-02-18 at 14.40.29.gif

傳統的思路肯定是透過JS去動態計算容器的高度,但這樣就涉及到載入時機的問題,獲取早了可能元素還沒渲染好,晚了又會有明顯的卡頓感,或者會引起頁面的閃爍。

那有沒有僅透過CSS的方法呢?

當然也是有的!要實現上面這個例子的效果,需要解決以下幾個問題:

  1. 如何判斷不同的高度?
  2. 如何在不同的高度下展示隱藏點選按鈕?
  3. 如何點選切換?

花幾分鐘一起看看吧?

一、先思考一下佈局

明確來講,CSS現在已經有相關方法可以判斷高度了,那就是CSS容器查詢。不過這個特性太高階了,目前幾乎還不能實戰,我們這次介紹一種更加傳統的方式。

如何判斷不同的高度?換句話來說,什麼樣的佈局在不同的高度下會有截然不同的效果

思考一下

?

?

絕對定位?位置完全固定了,不行。

flex佈局?好像也只能控制水平方向上

grid佈局?這個水太深,沒來得及研究(可能也行?)

等等,除了以上,還有一個現在都避而不談的浮動佈局,為啥現在都很少用了呢,原因在於浮動佈局非常脆弱,細小的尺寸變化都能引起整個佈局的坍塌。我記得以前用浮動佈局的時候,都需要尺寸精確,稍微出一點差錯就導致浮動元素不知道跑哪去了...

既然對尺寸非常敏感,是不是和本文的臨界高度有一定聯絡呢?

沒錯,今天要用到的方式就是浮動佈局

二、浮動佈局的奧妙

一步一步,來搭建我們所需要的頁面雛形。

我們先來看一個有趣的現象,這裡有一個容器,裡面有3個子節點,分別為ABC,其中A浮動BC右浮動

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

當橫向空間足夠時,效果是這樣的

image.png

此時,A貼近左邊,B貼近右邊,C貼著B

如果橫向空間不足,那麼C就會換行

image.png

現在C看似好像跑到了B的下方,其實是因為B的高度還沒有超過A

B的高度超過A時,那麼C會有如下表現

image.png

此時,C貼在B的左側,A的下方

是不是很神奇?除了浮動佈局,沒有什麼方法可以實現這樣的效果了吧。?那麼,這和本文的例子有什麼關係呢?

別急,其實這是一種極端情況,接著往下看

三、極端情況下的浮動表現

我們可以設定一個極限狀態,比如A的高度充滿容器,B的寬度充滿容器,此時B肯定會掉下來,我們用負的marginB仍保持在一行,如下

.a{
  width: 50px;
  height: 100%;
}
.b{
  width; 100%;
  margin-left: -50px;
}

image.png

此時,B的高度不高於A,所以C仍然是貼在B的下方,並且靠右。現在讓B的高度超過A,也就是超出容器高度,就變成了這樣

image.png

此時,C位於A的下方。也就是,僅僅因為高度超過了一個臨界值,C就得到了兩種截然不同的位置,如下

image.png

下面是動態演示(動態改變B的高度)

Kapture 2023-02-18 at 15.48.09.gif

試想一下,把C當做是“展開摺疊”按鈕,在這個基礎上挪動一下C的相對位置,移到正下方,是不是就是我們需要的效果了呢?下面的虛線框示意移動後的位置,這樣在視區範圍內,虛線框在高度不足時就是不可見的,只有在超過固定高度後才可見,示意如下(觀察虛線框的位置)

Kapture 2023-02-18 at 15.56.02.gif

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

效果如下:

image.png

然後是點選切換效果,可以用: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);
}

這樣就可以控制不同的狀態了

Kapture 2023-02-18 at 16.48.09.gif

還可以加點遮罩,讓點選處有一種淡出弱化的效果,表示別急,下面還有內容

.text{
     /* */
  -webkit-mask: linear-gradient(red 150px, transparent 200px);
}

效果如下

image.png

完整 demo 可以訪問以下任意連結

五、最後總結一下

想不到浮動佈局還能實現這樣的功能,總的來說,這是一種成本低廉但需要點想象力的實現方式,適應性和相容性也都不錯,下面總結一下

  1. 佈局有很多種,浮動佈局比較特殊
  2. 浮動佈局非常脆弱,細小的尺寸都能引起整個佈局的坍塌
  3. 超過指定高度後,由於浮動佈局引起的坍塌,正好可以區分兩種情況
  4. 透過改變按鈕本身的相對位置,可以讓案例在超出指定高度後才可見
  5. 點選切換可以用input:checklabel相關聯實現
  6. 淡出弱化效果可以新增一層蒙版mask,表示下面還有內容

最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤

歡迎關注我的公眾號:前端偵探

本文參與了SegmentFault 思否寫作挑戰賽,歡迎正在閱讀的你也加入。

相關文章