用純css實現Tab切換

XboxYan發表於2018-07-17

我們今天用css來實現一個常見的tab切換效果

檢視原文可以有更好的排版效果哦

先看效果

https://codepen.io/xboxyan/pe…

前言

哪些簡單的效果可以考慮用css來實現呢,目前css能夠做的互動有

  • 滑鼠經過/離開:hover
  • 滑鼠點選::cheked

那是不是上述所有的互動都可以用css來實現呢?顯然不是的,css實現依賴於html結構,因為css選擇器有限,有子選擇器,沒有父選擇器,有後選擇器,沒有前選擇器,所以侷限性很大。所以當我們思考能否用css來實現時還應考慮到html的結構,能不能構造出滿足css已存在的選擇器的html佈局。

先從html結構說起

一般在碰到tab型別的元件時,一般會有如下的html

<div class="tabs">
  <div class="tab-nav">
    <div class="tab-item">tab01</div>
    <div class="tab-item">tab02</div>
    <div class="tab-item">tab03</div>
  </div>
  <div class="tab-content">
    <div>111</div>
    <div>222</div>
    <div>333</div>
  </div>
</div>

大致就是這種佈局吧,暫且稱作分離模式吧,足夠靈活,就是導航部分和內容結構分開,需要什麼動畫效果都可以分別實現。

還有一種佈局,大概是這樣的

<div class="tabs">
  <div class="tab-pane">
    <div class="tab-item">tab01</div>
    <div class="tab-content">111</div>
  </div>
  <div class="tab-pane">
    <div class="tab-item">tab01</div>
    <div class="tab-content">111</div>
  </div>
  <div class="tab-pane">
    <div class="tab-item">tab01</div>
    <div class="tab-content">111</div>
  </div>
</div>

這裡每一個tab裡面的導航和內容是一起的,我們稱為合併模式吧,這種在元件化裡面很常見,比如在react裡面,一個tab元件,一般會寫成這樣

ReactDOM.render(
  <Tabs defaultActiveKey="1" onChange={callback}>
    <TabPane tab="Tab 1" key="1">Content of Tab Pane 1</TabPane>
    <TabPane tab="Tab 2" key="2">Content of Tab Pane 2</TabPane>
    <TabPane tab="Tab 3" key="3">Content of Tab Pane 3</TabPane>
  </Tabs>,
mountNode);

是不是很像?

相信用js只要是一名合格的前端都能輕易的實現吧,這裡我們主要是研究如何用css來實現

第一種佈局(分離模式)

這裡有兩種思路

  1. 錨點實現 herf + :target
  2. css3 nth-child(n) 選擇器

錨點實現主要是在a標籤加上href屬性,然後目標元素上新增相同的id,當點選a標籤時,目標元素的:target就生效了

<style>
#item01:target{ background:red }
</style>
<a href="#item01">跳轉</a>
<div id="item01">內容</div>

這種方式可以將導航和內容連結到一塊,但是在瀏覽器中有一個很不友好的地方,就是當點選帶有錨點的a連結的時候,瀏覽器會自動定位到目標位置,很影響體驗,需要去掉這種效果估計還得藉助js來實現,故不太推薦用這種方式

nth-child(n) 選擇器,這種方式就比較傳統了,不過導航切換部分還是用到label+input[type=radio],實現基本和js是同一個思路,甚至還不如js方便,因為你有多少個tab選項就得寫多少個nth-child樣式

.tab-nav input:nth-of-type(1):checked ~ .tab-content :nth-of-type(1),
.tab-nav input:nth-of-type(2):checked ~ .tab-content :nth-of-type(2),
.tab-nav input:nth-of-type(3):checked ~ .tab-content :nth-of-type(3),
...
{
  z-index:1
}

你大概會看到這樣的樣式,如果選項卡比較固定,基本是靜態的,比較少,可以一一寫出來,如果比較多,或者是js生成的,那麼建議這一部分樣式也通過js生成出來。

下面著重來實現第二種佈局

第二種佈局(合併模式)

如果是這樣一種佈局,那麼導航和內容就可以通過相鄰選擇器+聯絡上了,重點是如何實現選項卡的樣式,當然,我們也需要改一下html

<div class="tabs">
  <div class="tab-pane">
    <input type="radio" name="tab" id="tab01"/>
    <label class="tab-item" for="tab01">tab01</label>
    <div class="tab-content">111</div>
  </div>
  <div class="tab-pane">
    <input type="radio" name="tab" id="tab02"/>
    <label class="tab-item" for="tab02">tab02</label>
    <div class="tab-content">222</div>
  </div>
  <div class="tab-pane">
    <input type="radio" name="tab" id="tab03"/>
    <label class="tab-item" for="tab03">tab03</label>
    <div class="tab-content">333</div>
  </div>
</div>

然後我們通過樣式美化一下

.tabs{
  position:relative;
  width:400px;
  height:300px;
}
.tab-pane{
  display:inline-block;
}
.tabs input[type=`radio`]{
  position:absolute;
  clip:rect(0,0,0,0)
}
.tab-item{
  display:block;
  height:34px;
  line-height:34px;
  cursor:pointer;
  padding:0 10px
}

.tab-content{
  position:absolute;
  border:1px solid #eee;
  padding:20px;
  left:0;
  top:36px;
  bottom:0;
  right:0;
  background:#fff;
}

然後加入互動,主要就是相鄰選擇器+:checked選擇器,

.tabs input[type=`radio`]:checked+.tab-item{/**導航選中狀態**/
  background:orangered;
  color:#fff
}
.tabs input[type=`radio`]:checked+.tab-item+.tab-content{/**當前內容切換**/
  z-index:1
}

我們這裡只用了z-index:1就實現了隱藏顯示,當然還可以實現更多的效果,比如淡入淡出等

下面是演示效果

https://codepen.io/xboxyan/pe…

當然,這種方式也有一定的不足,由於這裡內容區域用到了絕對定位,所以整個tab容器就不能根據裡層的內容來自適應,也需要給外層一個固定的高度,不然整個tab容器就只有頂部導航區域才佔據空間,這明顯就不合常理。

新增一點動畫效果

/**給導航新增橫條的縮放效果**/
.tab-item:after{
  position:absolute;
  content:``;
  height:3px;
  width:100%;
  background:orangered;
  left:0;
  bottom:2px;
  transition:.3s;
  transform:scaleX(0)
}

.tabs input[type=`radio`]:checked+.tab-item:after{
  transform:scaleX(1)
}
/**給內容區域新增一個淡入淡出的效果**/
.tab-content{
  position:absolute;
  background:#eee;
  padding:20px;
  left:0;
  top:36px;
  bottom:0;
  right:0;
  transition:.3s;
  opacity:0;
  transform:translateY(50px)
}

.tabs input[type=`radio`]:checked+.tab-item+.tab-content{
  z-index:1;
  opacity:1;
  transform:translateY(0)
}

https://codepen.io/xboxyan/pe…

小節

通過css我們也能實現導航效果,而且更容易複用,只需要複製html結構就行(當然這裡肯定也是需要改一下nameid的)。這就有點類似元件的意思了,給你一個html結構,你不需要關係切換邏輯,只需要根據介面取到資料,然後塞到每個tab標籤頁裡面去,事實上現在react實現的元件也一般都是這種思路,只需關注業務邏輯,但這些都是大工程,哪裡有css直接來的快。

這就讓我想到了剛進公司那會,每碰到一個tab,那就要取一個id,然後用jquery實現一遍tab切換邏輯,後來放聰明瞭,把tab封裝成一個外掛,碰到一個tab就呼叫一次外掛…看著程式碼變少了,其實也沒什麼本質區別。

用css來實現的好處就是可以儘量大的把元件功能和業務邏輯分離開來,真正做一個UI元件該做的事,希望css越來越好

相關文章