【web開発 CSS】ドロップダウンメニュー4作品

#はじめに
最近、ずっとほったらかしにしてきたCSSでデザインの勉強。webデザイナーになりたいわけではないので将来どこかweb系に就職して使うかは分からないが、最低限の知識はいるだろうと思って最近サンプルを作っている。入社すぐはフロントサイドとか担当させてもらった時とかに活用できそう。
![demo](https://media.giphy.com/media/UqSBuez6vM05cLULd8/giphy.gif)
今回4種類のホバー型ドロップダウンメニューを作成。左からItem1,2,3,4。以下コードの掲載と解説。

まずItemの解説をする前にhtmlと共通のcssを以下に記載しておく。なお、こちらには今回の趣旨であるドロップダウンメニューに関するコードだけ記載している。全体のコードは[GitHub](https://github.com/KengoShimizu/dropdownmenu/tree/master)にあげておく。

```html:html
<body>
    
   <ul class="sub2">
      <li><a href="#">Sub-menu01</a></li>
      <li><a href="#">Sub-menu02</a></li>
      <li><a href="#">Sub-menu03</a></li>
   </ul>

   <ul class="sub3">
      <li><a href="#">Sub-menu01</a></li>
      <li><a href="#">Sub-menu02</a></li>
      <li><a href="#">Sub-menu03</a></li>
   </ul>

   <div class="sub4_box"></div>
   <ul class="sub4">
      <li><a href="#">Sub-menu01</a></li>
      <li><a href="#">Sub-menu02</a></li>
      <li><a href="#">Sub-menu03</a></li>
   </ul>


   <header>
      <section id="title">Title</section>
      <ul class="menu_bar">

         <li class="menu_items">
            item1
            <div class="underbar">
               <ul class="sub1">
                  <li><a href="#">Sub-menu01</a></li>
                  <li><a href="#">Sub-menu02</a></li>
                  <li><a href="#">Sub-menu03</a></li>
               </ul>
            </div>
         </li>

         <li class="menu_items">
         item2
         <div class="underbar"></div>
         </li>

         <li class="menu_items">
         item3
         <div class="underbar"></div>
         </li>

         <li class="menu_items">
         item4
         <div class="underbar"></div>
         </li>

      </ul>
   </header>
</body>
```

```css:css
header{
	z-index: 3; /*下からmain,menu,headerの順番*/
	position: relative;
	position: fixed; /*header固定*/
  	top: 0;
  	left: 0;
	height: 120px;
	width: 100%;
	background-color: #696969;
}

ul{
	z-index: 2; /*下からmain,menu,headerの順番*/
	padding: 0; /*左のpadding削除*/
	list-style: none; /*デフォルトのちょぼ消す*/
}

a{
	display: block;
	font-size: 0.8em;
	text-transform: uppercase;
	letter-spacing: .2em;
}

ul.menu_bar{
	height: 40px;
	padding: 0; /*左のpadding削除*/
	padding-top: 10px;
	margin: 0;
	display: flex;
	justify-content: center; /*アイコン、テキストボックスを横方向の中心に*/
}
```

##underbarクラス
これは各Itemの下に引いてあるアンダーバーであり、わざわざ各Itemとは別の要素で用意した。この理由は、Itemにborder-bottomを設定した場合にドロップダウンメニューとItemに隙間を開けないようにすると、Itemのborder-bottomとドロップダウンメニューの上部が重なり見栄えが悪くなるから。

#Item1
こちらはシンプルなドロップダウンメニュー。メニューの幅はItem1の要素と同じで、表示するときに少し下がりながらfadeinしてくる感じ。


```css:css
ul.sub1{
	position: absolute;
	visibility: hidden; /*hover前は非表示*/
	opacity: 0; /*透明*/
	top: 0; 
	left: 0;
	width: 100%;
	background: white;
	transition: all 0.5s;
    -webkit-transition: all 0.5s;
}

ul.sub1 > li > a{
	padding: 10px 10px;
}


.menu_items:nth-child(1):hover > div > ul.sub1{
	top: 10px;
	visibility: visible; /*表示*/
    opacity: 1;
}
```

##hoverの謎
たぶんこれが一番王道のドロップダウンメニューかな？正直これを作っててなんでItemからhoverが外れてドロップダウンメニューにカーソルが写ってもドロップダウンメニューが消えないのかはよくわかっていない。どういう仕組みか分かる方いればご教授願います。

#Item2
このドロップダウンメニューもよくwebサイトで見かけるような気がする。

```css:css
ul.sub2{
	position: absolute; /*横幅をheaderと同じにするためheaderの子要素*/
	display: flex; /*横並び*/
	justify-content: center; /*アイコン、テキストボックスを横方向の中心に*/
	width: 100%;
	height: 100px;
	top: 20px; /*画面外*/
	left: 0;
	margin: 0; /*headerとsub2との隙間をなくす*/
	background-color: white;
}

ul.sub2 > li > a{
	padding: 30px 40px;
}
```
```javascript:jquery
$('header > ul > li:eq(1)').hover(function(){
	//hoverした時
	$('.sub2').css({
		//transformの設定
		'transform': 'translate3d(0, 100px, 0)',
		'transition': 'all 0.2s',
		'-webkit-transition': 'all 0.2s'
	});
},function(){
	//hoverが解除された時
	//これはitem2からmouseleaveした直後の数回取得する
	$(':hover').each(function(){
		//headerとitem2の間にundefinedがあるため
		if ($(this).attr('class') != undefined){
			if ($(this).attr('class') != 'sub2'){
				$('.sub2').css({'transform': 'translate3d(0, 0, 0)'});
			}
		}
	});

});

$('.sub2').on('mouseleave',function(){
	//transformの上書き
	$('.sub2').css({'transform': 'translate3d(0, 0, 0)'});
});
```

##jsとcssでのインデックスの扱い
Item1のcssで書いているように、要素を数えるときcssでは1から始まる。jsは0から始まる。

##ドロップダウンメニューの収納
今回Item2,3,4で同じ手法でドロップダウンメニューを収納している。遠回りなやり方かもしれないが他に方法が思いつかなかったのでもっと調べる必要あり。具体的にはjqueryの$(':hover')のところであるが、カーソルがItemから外れた瞬間のカーソルがhoverしている要素がドロップダウンメニュー上であればそのまま表示、それ以外であれば収納、といった感じ。またドロップダウンメニューにはmouseleaveで収納するようになっているため、カーソルがItemかドロップダウンメニューから離れると収納する。

#Item3
こちらの他と違う点はドロップダウンメニューの表示・収納時に他の画面も全て動く点。cssに関してはItem2の時と(クラス名がsub3と言う点以外)全く同じなので省略。

```javascript:jquery
$('header > ul > li:eq(2)').hover(function(){
	//hoverした時
	$('.sub3').css({
		//transformの設定
		'transform': 'translate3d(0, 100px, 0)',
		'transition': 'all 0.2s',
		'-webkit-transition': 'all 0.2s'
	});
	$('main').css({
		//transformの設定
		'transform': 'translate3d(0, 100px, 0)',
		'transition': 'all 0.2s',
		'-webkit-transition': 'all 0.2s'
	});
},function(){
	//hoverが解除された時
	//これはitem3からmouseleaveした直後の数回取得する
	$(':hover').each(function(){
		//headerとitem3の間にundefinedがあるため
		if ($(this).attr('class') != undefined){
			if ($(this).attr('class') != 'sub3'){
				$('.sub3').css({'transform': 'translate3d(0, 0, 0)'});
				//mainが動くとz-indexが一番大きくなるため固定してitem1,2が後ろにいかないようにする
				$('main').css({
					'transform': 'translate3d(0, 0, 0)',
					'z-index': '1'
				});
			}
		}
	});

});

$('.sub3').on('mouseleave',function(){
	//transformの上書き
	$('.sub3').css({'transform': 'translate3d(0, 0, 0)'});
	$('main').css({
		'transform': 'translate3d(0, 0, 0)',
		'z-index': '1'
	});
});
```

##z-indexの謎
ここでも正直なぜか理解していないことが発生。それはmainをtranslateで動かした後なぜかmainが画面最上部に表示されてしまうということ。よってz-indexを再設定しないと、Item3のドロップダウンメニューを表示させた後、Item1や2のドロップダウンメニューがmainの後ろ側にいってしまう。ドロップダウンメニューは動かしてもz-index変わらんのになぜ？これも分かる方いれば是非ご教授ください。

#Item4
こちらはアニメーションを効かせて見栄えを工夫したもの。

```css:css
ul.sub4{
	position: absolute; /*横幅をheaderと同じにするためheaderの子要素*/
	display: flex; /*横並び*/
	justify-content: center; /*アイコン、テキストボックスを横方向の中心に*/
	width: 100%;
	height: 90px; /*sub4_boxよりも小さくすることでmouseleaveを正常に効かせる*/
	top: 20px; /*画面外*/
	left: 0;
	margin: 0; /*headerとsub2との隙間をなくす*/
}

.sub4_box{
	z-index: 2; /*下からmain,menu,headerの順番*/
	position: absolute; /*横幅をheaderと同じにするためheaderの子要素*/
	width: 100%;
	height: 100px;
	top: 20px; /*画面外*/
	left: 0;
	background-color: white;
}

.sub4_box + ul > li > a{
	padding: 30px 40px;
}
```
```javascript:jquery
$('header > ul > li:eq(3)').hover(function(){
	//hoverした時
	$('.sub4_box').css({
		//transformの設定
		'transform': 'translate3d(0, 100px, 0)',
		'transition': 'all 0.2s',
		'-webkit-transition': 'all 0.2s'
	});

	let time = 0.4;
	for (let i=0; i<3; i++){
		$('body > ul:eq(2) > li:eq('+i+')').css({
			//transformの設定
			'transform': 'translate3d(0, 100px, 0)',
			'transition': 'all '+time+'s',
			'-webkit-transition': 'all '+time+'s'
		});
		time += 0.3;
	}
},function(){
	//hoverが解除された時
	//これはitem2からmouseleaveした直後の数回取得する
	$(':hover').each(function(){
		//headerとitem2の間にundefinedがあるため
		if ($(this).attr('class') != undefined){
			if ($(this).attr('class') != 'sub4'){
				for (let i=0; i<3; i++){
					$('body > ul:eq(2) > li:eq('+i+')').css({
						//transformの設定
						'transform': 'translate3d(0, 0, 0)'
					});
				}
				//listが動くのを待つため0.3秒遅らせる
				setTimeout(function(){
					$('.sub4_box').css({'transform': 'translate3d(0, 0, 0)'});
				},300);
			}
		}
	});

});

$('.sub4_box').on('mouseleave',function(){
	//transformの上書き
	for (let i=0; i<3; i++){
		$('body > ul:eq(2) > li:eq('+i+')').css({'transform': 'translate3d(0, 0, 0)'});
	}
	setTimeout(function(){
		$('.sub4_box').css({'transform': 'translate3d(0, 0, 0)'});
	},300);
});

```
特に難しいことはしていない。強いて言うなら初めてsetTimeout()メソッド使ったくらい。

#最後に
今回はドロップダウンメニューを作ってみた。ブロック要素の扱いにだいぶ慣れてきた気がする。あとはいろんなwebサイト参考にしておしゃれなデザインができるようになりたい。