CSS horizontal menu
前言
四年前要我這個後端型工程師用CSS排出一個水平選單,算是一個不可能的任務XD
四年後,這段時間去恆逸上課(※這不是業配文),我從基本的CSS、DIV排版開始學習,學到現在很潮的RWD網頁排版
加上最近案子有改版成RWD網頁需求,讓我CSS排版功力大增,也讓我意識到,以前學校教的Table排版,害我不淺Orz,出社會工作有很長一段時間,我還在使用Table的思維排版網頁....
資訊技術這領域的知識果然得定期更新才行,上課學過CSS+DIV排版後才知道,表格(ex.履歷表)這類的版型,原來也可以只用DIV排版XD
實作
本文主要記錄水平選單的HTML、CSS排版
1.最古早期的基本款
<body>
<ul>
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#" class="last_a">AAA</a>
</li>
</ul>
</body>
body{
padding:0;
margin:0;
/*body負責讓選單水平垂直置中*/
height:100vh;
display:flex;
justify-content:center;
align-items:center;
}
ul{
/*ul預設會有些微的margin、padding*/
margin:0;
padding:0;
list-style:none;
}
ul > li{
/*讓選單變成水平的關鍵*/
float:left;
}
ul > li > a{
/*a元素預設display:inline;不支援width、height*/
display:inline-block;
text-decoration:none;
width:100px;
height:50px;
line-height:50px;/*文字垂直置中*/
text-align:center;/*文字水平置中*/
border:1px solid #d2d2d2;
border-right-width:0px;
color: #434eae;/*前景色深藍色*/
background: #fff;/*背景色白色*/
}
ul> li > a.last_a{
border-right-width:1px;
}
ul > li > a:hover{
/*當滑鼠hover時的變化*/
color: #fff;/*前景色白色*/
background: #434eae;/*背景色藍色*/
}
水平選單主要關鍵在 li 元素要設 float:left;就可以了
另外如果 li 使用 display:inline-block; 也可以變成水平選單,但要留意 li 間隔4px的地雷:移除 CSS inline-block 空白
※最近專案遇到的經驗:使用 float:left;在容器width不夠時會強行自動換行,即使容器加上 white-space:nowrap 仍舊會自動換行,這時想讓元素靠左就不該用float:left,而應該使用display:inline-block;取代之。
線上Live Demo:https://jsfiddle.net/ShadowKao/2z0uv7tq/
2019.1.6追加純CSS的第二層選單Sample Code:
<body>
<ul class="outerUl">
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#">AAA</a>
<ul class="innerUl">
<li>
<a href="#">BBB1</a>
</li>
<li>
<a href="#">BBB2</a>
</li>
<li>
<a href="#">BBB3</a>
</li>
<li>
<a href="#">BBB4</a>
</li>
</ul>
</li>
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#" class="last_a">AAA</a>
</li>
</ul>
</body>
body{
padding:0;
margin:0;
/*body負責讓選單水平垂直置中*/
height:100vh;
display:flex;
justify-content:center;
align-items:center;
}
ul
{
/*ul預設會有些微的margin、padding*/
margin:0;
padding:0;
list-style:none;
}
ul.outerUl > li{
/*讓第一層選單變成水平的關鍵*/
float:left;
}
ul.outerUl > li > a,
ul.innerUl > li > a{
/*a元素預設display:inline;不支援width、height*/
display:inline-block;
text-decoration:none;
width:100px;
height:50px;
line-height:50px;/*文字垂直置中*/
text-align:center;/*文字水平置中*/
border:1px solid #d2d2d2;
color: #434eae;/*前景色深藍色*/
background: #fff;/*背景色白色*/
}
/*外層選單*/
ul.outerUl > li > a{
border-right-width:0px;
}
/*內層選單*/
ul.innerUl > li > a{
border-top-width:0px;
}
ul.outerUl> li > a.last_a{
border-right-width:1px;
}
ul.outerUl > li > a:hover,
ul.innerUl > li > a:hover{
/*當滑鼠hover時的變化*/
color: #fff;/*前景色白色*/
background: #434eae;/*背景色藍色*/
}
ul.outerUl > li {
position:relative;
}
ul.innerUl{
display:none;/*第二層選單容器預設隱藏*/
position:absolute;
z-index:10;
}
ul.outerUl > li a:hover ~ ul.innerUl ,
ul.innerUl:hover
{
display: block;/*當滑鼠hover時,接續在a之後的所有第二層選單容器ul才顯示*/
}
線上Demo: https://jsfiddle.net/ShadowKao/ynvra3c5/
以上是最常見的基本款,但自從CSS3出現之後,各種transition轉場效果層出不窮,最常看到的是滑鼠hover時,背景色淡入淡出
2.選單transition效果版↓
body{
padding:0;
margin:0;
/*body負責讓選單水平垂直置中*/
height:100vh;
display:flex;
justify-content:center;
align-items:center;
}
ul{
/*ul預設會有些微的margin、padding*/
margin:0;
padding:0;
list-style:none;
}
ul > li{
/*讓選單變成水平的關鍵*/
float:left;
}
ul > li > a{
/*a元素預設display:inline;不支援width、height*/
display:inline-block;
text-decoration:none;
width:100px;
height:50px;
line-height:50px;/*文字垂直置中*/
text-align:center;/*文字水平置中*/
border:1px solid #d2d2d2;
border-right-width:0px;
color: #434eae;/*前景色深藍色*/
background: #fff;/*背景色白色*/
transition:0.4s;/*a元素會改變背景色,對a加上transition即可*/
}
ul> li > a.last_a{
border-right-width:1px;
}
ul > li > a:hover{
/*當滑鼠hover時的變化*/
color: #fff;/*前景色白色*/
background: #434eae;/*背景色藍色*/
}
線上Live Demo:https://jsfiddle.net/ShadowKao/uncozqd2/
關於transition的個人心得:
1.通常transition都直接設在要特效的元素上
例如上述的 ul > li > a { transition:0.4s; },只要寫這一行,同個元素的滑鼠、點擊互動狀態就會自動繼承
如此一來, :hover、:focus、:active....等狀態的元素,就不用囉嗦地重覆寫一遍transition(除非每種互動狀態你想做不同的特效)
ul > li > a:hover,
ul > li > a:active ,
ul > li > a:focus { transition:0.4s; }
2. transition時間最常見為0.3s~1s,超過1s就會開始覺得慢
3.淡入淡出特效不可以使用 display:none;和display:block; 必須使用opacity:0和opacity:1才行
如果只想要背景色淡入淡出的話就改使用 background-color: rgba(0,0,0,0);最後一個數字的設定:https://jsfiddle.net/ShadowKao/1s32vL5x/
4.transition只能配合滑鼠、使用者點擊的互動特效上,我還沒見過網頁一載入就執行transtion特效,如果要實現網頁一載入就執行動畫
就要使用CSS3的動畫property:animation-name、@keyframes、animation-duration來實現,例如:Animate.css、WOW.js
但反過來看,當滑鼠、使用者點擊的互動特效,transition可以改寫成 animation-name、@keyframes、animation-duration等等CSS3 的 「animation-*」動畫 property來替代實現
但此時就會感受到transition和animation-*之間的明顯差異:網頁一載入時,animation-* 會自動執行動畫,transition則不會
如果硬要消除animation-*這點特性,就得依靠JS幫助,請見線上Demo:https://jsfiddle.net/ShadowKao/3f8krx2s/ (順便留意當動畫播放途中,滑鼠立即離開div區塊時,transition和animation-*的差異)
↑從線上Demo可見寫起來頗囉嗦,所以和使用者互動的動畫特效因此才會比較常看到transition的寫法吧
※當動畫播放途中,滑鼠一離開div區塊(強制結束、變更動畫執行),transition會由當前狀態反向播放回原始狀態,但animaion-*則會立即跳到最後狀態再反向播放回原始狀態,也就是說transtion的播放被中斷時,特效比較順暢。
※相關討論:How to reverse an animation on mouse out after hover
transiton也有delay時間可以設定,詳細設定如下↓
預設值
transition-property:all; /*轉場效果套用在全部的CSS property上*/
※不知道transition-property意義的請見: transition-property | MDN
transition-timing-function:ease; /*慢速開始,然後變快,然後慢速结束*/
這兩個預設值就很夠用,所以常常看看transition簡寫成 transition: 0.4s; /*只有寫執行時間*/
更多transition炫技影片在此:Close | CSS Text Transition Hover Effects
3.選單搭配偽元素的特效↓
<body>
<ul>
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#">AAA</a>
</li>
<li>
<a href="#" class="last_a">AAA</a>
</li>
</ul>
</body>
body{
padding:0;
margin:0;
/*body負責讓選單水平垂直置中*/
height:100vh;
display:flex;
justify-content:center;
align-items:center;
}
ul{
/*ul預設會有些微的margin、padding*/
margin:0;
padding:0;
list-style:none;
}
ul > li{
/*讓選單變成水平的關鍵*/
float:left;
}
ul > li > a{
/*a元素預設display:inline;不支援width、height*/
display:inline-block;
text-decoration:none;
width:100px;
height:50px;
line-height:50px;/*文字垂直置中*/
text-align:center;/*文字水平置中*/
border:1px solid #d2d2d2;
border-right-width:0px;
color: #434eae;/*前景色深藍色*/
background: transparent;/*背景色透明,避免蓋掉偽元素*/
position:relative;
transition:0.4s;/*a元素會改變文字顏色為白色,對a加上transition*/
box-sizing:border-box;/*稍微修正偽元素無法蓋滿a元素的問題*/
}
ul> li > a.last_a{
border-right-width:1px;
}
ul > li > a:hover
{
color: #fff;/*前景色白色*/
}
ul > li > a:before
{/*為a插入偽元素*/
content:"";
position:absolute;
width:100%;
height:100%;
top:0;
left:0;/*把偽元素移至正確位置,靠左上*/
z-index:-1;/*把偽元素變成背景*/
background: #434eae;/*背景色藍色*/
/*border-radius:4px;此Sample沒有圓角,拿掉*/
/*下面三個property和特效有關*/
transition:0.4s;/*特效執行時間*/
transform:scaley(0);/*預設垂直縮放不見*/
opacity:0;/*預設透明*/
}
ul > li > a:hover:before{
/*當滑鼠hover時的變化*/
transform:scaley(1);/*垂直縮放恢復原貌*/
opacity:1;/*變成不透明*/
}
線上Demo: https://jsfiddle.net/ShadowKao/ut94ya01/
偽元素在CSS3的正確寫法為::before、::after,但為了IE瀏覽器的相容,我自己會寫成:before、:after
偽元素的出現位置
注意定義偽元素時,一定要加上content:""; 偽元素才會出現,而且content:"<b>test</b>";裡面放置的字串會被瀏覽器自動HtmlEncode,所以無須擔心XSS攻擊
還有偽元素預設display:inline,不支援width、height,如果使用在排版堆疊上記得加上display:block;或display:inline-block;才支援寬高
但因為本文Sample的偽元素宣告為position:absolute; 所以不用額外定義display也能支援width、height。
討論文:What is the default display property of the :before and :after pseudo-elements?
由於偽元素位置為inside container,所以不支持<input type="text">、<img>應該也不支持:Can I use a :before or :after pseudo-element on an input field?
但神奇的是Chrome支持<input type="checkbox" />和<input type="radio" />
所以網路上就出現這樣一個純CSS把checkbox改造成switch開關的影片:Css Custom Animated Checkbox - How to make CSS switch / toggle / custom checkbox - No Javascript
其他文章參考:
Default CSS Values for HTML Elements
CSS3 Transition客製化參數線上工具:Ceaser CSS EASING ANIMATION TOOL