css實現依賴于html結構,因為css選擇器有限,有子選擇器,沒有父選擇器,有后選擇器,沒有前選擇器,所以局限性很大。所以當我們思考能否用css來實現時還應考慮到html的結構,能不能構造出滿足css已存在的選擇器的html布局
前言
哪些簡單的效果可以考慮用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來實現
第一種布局(分離模式)
這里有兩種思路
- 錨點實現?
herf
?+?:target
- 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
就實現了隱藏顯示,當然還可以實現更多的效果,比如淡入淡出等
演示效果見頁底Demo2
當然,這種方式也有一定的不足,由于這里內容區域用到了絕對定位,所以整個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)
}
演示效果見頁底Demo3
小節
通過css我們也能實現導航效果,而且更容易復用,只需要復制html結構就行(當然這里肯定也是需要改一下name
和id
的)。這就有點類似組件的意思了,給你一個html結構,你不需要關系切換邏輯,只需要根據接口取到數據,然后塞到每個tab標簽頁里面去,事實上現在react
實現的組件也一般都是這種思路,只需關注業務邏輯,但這些都是大工程,哪里有css直接來的快。
這就讓我想到了剛進公司那會,每碰到一個tab,那就要取一個id
,然后用jquery
實現一遍tab切換邏輯,后來放聰明了,把tab封裝成一個插件,碰到一個tab就調用一次插件…看著代碼變少了,其實也沒什么本質區別。
用css來實現的好處就是可以盡量大的把組件功能和業務邏輯分離開來,真正做一個UI組件
該做的事,希望css越來越好