[CSS] 水平選單的排版及滑鼠hover時的效果整理 (加上一些transition、偽元素的個人使用心得)

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.cssWOW.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