今回はWebサイトで多く使用されるUIデザインの一つ、タブメニューの実装方法をまとめました。
いまいち仕組みが分かってないのでコーディングにつまずいてる、なんて方もいるかと思いますが、当記事で紹介するコードはコピペもできるようにしています。
簡単に解説も入れているので、コーディングの参考にしたい方は是非ご覧ください。
それではいってみましょう!
タブメニューとは
下のGif画像のように、タブ形状のメニューをホバーやクリック、タップすることでコンテンツが切り替わるUIのことです。
料金メニューをプラン別に切り替えて表示させたり、ヘッダーメニューをホバーして切り替えたりと、省スペースで情報を制御できる汎用性の高いデザインです。
今回は3パターン作成していきます。デモページも用意しているので、完成版はこちらからご覧ください。
タブメニューをhoverで切り替え
HTML
<nav class="wrap">
<ul class="tab-container">
<li class="tab current">メニュー1</li>
<li class="tab">メニュー2</li>
<li class="tab">メニュー3</li>
<li class="tab">メニュー4</li>
</ul>
<div>
<ul class="menu-box">
<li class="menu">メニュー1-1</li>
<li class="menu">メニュー1-2</li>
<li class="menu">メニュー1-3</li>
<li class="menu">メニュー1-4</li>
</ul>
<ul class="menu-box">
<li class="menu">メニュー2-1</li>
<li class="menu">メニュー2-2</li>
<li class="menu">メニュー2-3</li>
<li class="menu">メニュー2-4</li>
</ul>
<ul class="menu-box">
<li class="menu">メニュー3-1</li>
<li class="menu">メニュー3-2</li>
<li class="menu">メニュー3-3</li>
<li class="menu">メニュー3-4</li>
</ul>
<ul class="menu-box">
<li class="menu">メニュー4-1</li>
<li class="menu">メニュー4-2</li>
<li class="menu">メニュー4-3</li>
<li class="menu">メニュー4-4</li>
</ul>
</div>
</nav>
- ulタグ→tab-containerクラス
- liタグ→tabクラス
- ulタグ→menu-boxクラス
- liタグ→menuクラス
一つ目のタブにはcurrentクラスをつけていること、menu-boxの親要素としてdivタグで包んでいることはそれぞれ後ほど説明します。
CSS
* {
margin: 0;
padding: 0;
box-sizing: border-box;
list-style: none;
}
@media screen and (max-width: 600px) {
body {
font-size: 2.5vw;
}
}
.wrap {
padding-top: 3rem;
width: 90%;
max-width: 1000px;
margin: 0 auto;
}
/************* 共通スタイルここまで **************/
/************* タブ *************/
.tab-container {
display: flex;
border-bottom: 3px double #2e2d2d
}
.tab {
position: relative;
padding: 1em;
border-radius: 10px 10px 0 0;
background-color: #ddd;
transition: all .2s;
cursor: pointer;
}
.tab:not(:last-of-type) {
margin-right: 1em;
}
/* タブ現在値 */
.current {
color: #eaeaea;
background-color: #3972a7;
}
.current::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 3px;
background-color: #28295e;
}
/************* メニュー *************/
.menu-box {
width: 100%;
}
.menu {
padding: 2em 1em;
background-color: #eee;
transition: all .2s;
cursor: pointer;
}
※hoverのスタイルは割愛してます。
ここまででこんな感じになっているかと思います。
一つ目のタブにcurrentクラスを設けていますが、これは今開いているタブのスタイルの為のクラスなので、jQueryで付け替えることになります。
jQuery
ここでは載せませんが、jQueryを使うのでCDNコードはHTMLのbodyの閉じタグ前に読み込んでおきましょう!詳しくはこちらから↓
$(function () {
//////////// 一番目以外のコンテンツは非表示
$(".menu-box:not(:first-of-type)").css("display", "none");
//////////// タブの制御
$('.tab').hover(function () { // タブメニューをhoverしたら
var index = $('.tab').index(this); // hoverしたタブ番号を取得
$('.tab').removeClass('current'); // タブ現在地クラスを削除し、
$(this).addClass('current'); // hoverしたタブにタブ現在地クラスを付与
//////////// コンテンツの制御
$('.menu-box').hide().eq(index).show(); // hoverしてないコンテンツは非表示、hoverした番号は表示
});
});
一行ずつ分解して解説します。
$(".menu-box:not(:first-of-type)").css("display", "none");
一つ目は初めから表示させておくので、.menu-boxの内一番目以外をdisplay:noneで非表示するという内容を先に記述しています。これはCSSに書いてもいいですが、今回はjQuery制御します。
ちなみに先ほどHTMLの項で4つのmenu-boxクラスをdivで囲っていたのはこれが理由です。タブメニューの話とは逸れますが一応解説しておきます。興味の無い方は読み飛ばしてもらって大丈夫です。
今回のHTML構造の場合、もしdivで囲わなかった場合の階層は下のようになります。
<nav class="wrap">
<ul class="tab-container"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
</nav>
first-of-typeはクラスではなく要素に対して判定されるので、今回はmenu-boxクラスがついてる要素→ulタグと判定します。
その判定された要素の兄弟要素内にあるulタグの一番目にスタイル指定がされることになります。
.menu-box:not(:first-of-type) {
display: none;
}
この場合一番目のulタグは.tab-containerクラスがついてるulタグです。これに指定されることになってしまい、それ以外のulタグにdisplay: noneがかかってしまいます。
なので下記のようにulタグの階層をズラすことでクリアされる、ということです。(そもそもtab-containerクラスの要素がulタグじゃなければこんなことしなくてもいいです)
<nav class="wrap">
<ul class="tab-container"> <!-- 略 --> </ul>
<div>
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
</div>
</nav>
hoverメソッドを使って、tabクラスの要素をhoverした時に〜する、という記述を書いていきます。
$('.tab').hover(function () {
タブの装飾
タブメニューは表示されてるメニュー(コンテンツ)と連動して「今何番目のタブが選択されているか?」が分かるように装飾される事が殆どです。
hoverしたタブのインデックス番号を取得して変数indexに代入します。
var index = $('.tab').index(this);
タブをhoverしたら予め一番目のタブにつけているcurrentクラスを外します。
$('.tab').removeClass('current');
hoverしたタブに現在地クラスcurrentを付与します。
$(this).addClass('current');
メニュー(コンテンツ)の制御
hoverしてないコンテンツは非表示、hoverしたコンテンツは表示するという内容です。
$('.menu-box').hide().eq(index).show();
メソッドチェーンを使って書いてるので分解すると下記の通り。
$('.menu-box').hide();
$('.menu-box').eq(index).show();
- menu-boxはhideで非表示
- hoverしたタブのindex番号と同じmenu-box番号を表示させる
これで完成になります。
タブメニューをclickでフェード切り替え
次は、クリックでタブメニューを切り替えて、その際にフワッと表示させる演出を加えます。
大枠のコードはほとんど変わりません。
HTMLではmenu-boxの親要素で設定していたdivタグにmenu-containerという名前でクラスをつけます。
<div class="menu-container">
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
</div>
CSSは下記を追記してください。
menu-containerにposition: relative、menu-boxにposition: absoluteで位置を重ね合わせます。
/* メニュー */
.menu-container {
/* fadein・outのために指定 */
position: relative;
}
.menu-box {
width: 100%;
/* fadein・outのために指定 */
position: absolute;
top: 0;
}
jQueryは6行目をクリックイベントに変えて、十二行目のshowメソッドをfadeInメソッドに変えただけです。
fadeInの()内に数値を指定すれば表示される感じ(フワッと出てくるスピード)を変えられます。
$(function () {
//////////// 一番目以外のコンテンツは非表示
$(".menu-box:not(:first-of-type)").css("display", "none");
//////////// タブの制御
$('.tab').on('click', function () { // タブメニューをhoverしたら
var index = $('.tab').index(this); // hoverしたタブ番号を取得
$('.tab').removeClass('current'); // タブ現在地クラスを削除し、
$(this).addClass('current'); // hoverしたタブにタブ現在地クラスを付与
//////////// コンテンツの制御
$('.menu-box').hide().eq(index).fadeIn();
});
});
ちなみに前項のコードのまま(hoverメソッド)でフェード指定をすると、タブから外れるたびにmenuが一瞬消えてしまいます。
フェードで切り替えたい場合は、position指定する必要があるので注意が必要です。
タブメニューをclickでスライド切り替え
メニューをスライド形式で表示させます。
こちらは先ほどの内容と比べて記述内容がかなり変わってきますが、原理については以前書いたカルーセルスライダーの記事で細かく解説してるので、詳細を確認したい方は良かったらこちらからご覧ください。
ざっくりとだけ説明すると、コンテンツ(メニュー)領域の階層を「表示枠」「横並びにしたスライドのレール」「スライド」と3つに分けます。
<div class="menu-wrap">
<div class="menu-container">
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
<ul class="menu-box"> <!-- 略 --> </ul>
</div>
</div>
分かりづらいかもしれないので全体も載せておきます。
<nav class="wrap">
<ul class="tab-container">
<li class="tab current">メニュー1</li>
<li class="tab">メニュー2</li>
<li class="tab">メニュー3</li>
<li class="tab">メニュー4</li>
</ul>
<div class="menu-wrap">
<div class="menu-container">
<ul class="menu-box">
<li class="menu">メニュー1-1</li>
<li class="menu">メニュー1-2</li>
<li class="menu">メニュー1-3</li>
<li class="menu">メニュー1-4</li>
</ul>
<ul class="menu-box">
<li class="menu">メニュー2-1</li>
<li class="menu">メニュー2-2</li>
<li class="menu">メニュー2-3</li>
<li class="menu">メニュー2-4</li>
</ul>
<ul class="menu-box">
<li class="menu">メニュー3-1</li>
<li class="menu">メニュー3-2</li>
<li class="menu">メニュー3-3</li>
<li class="menu">メニュー3-4</li>
</ul>
<ul class="menu-box">
<li class="menu">メニュー4-1</li>
<li class="menu">メニュー4-2</li>
<li class="menu">メニュー4-3</li>
<li class="menu">メニュー4-4</li>
</ul>
</div>
</div>
</nav>
menu-boxを横に並べて、それをmenu-containerとして包みます。タブメニューとして表示されている領域はmenu-wrapクラスということになります。
.wrap {
padding-top: 3rem;
width: 90%;
max-width: 1000px;
margin: 0 auto;
overflow: hidden;
}
.menu-wrap {
width: 1000px;
height: calc(1000px * 0.5625);
position: relative;
}
.menu-container {
position: absolute;
height: 100%;
}
.menu-box {
width: 1000px;
height: 100%;
float: left;
}
.menu {
width: 100%;
max-width: 1000px;
padding: 2em 1em;
background-color: #eee;
transition: all .2s;
cursor: pointer;
}
menu-boxはfloatで横並びにしているので画面外まではみ出し、横スクロールが発生しますので、wrapクラスにはoverflow: hiddenを指定しています。
あとはmenu-wrapをposition: relative、menu-containerをposition: absoluteにしないといけません。
また、それぞれの項目にwidthを指定する必要があるので追加しています。menu-containerはmenu-boxの幅×スライド数の合計幅になるので、計算してここで指定してもいいんですが、前回カルーセル記事で紹介している通りjQueryで計算して代入することにします。
///// コンテンツ全体の合計幅を計算
var width = $('.menu-box').outerWidth(true); // menu-box1枚分の幅
var length = $('.menu-box').length; // .menu-boxの数
var totalWidth = width * length; // レール全体幅 = menu-box1枚の幅 × スライド合計数
// .menu-containerに合計の幅を代入(※タブ数が増えた時のためCSSで指定しない)
$('.menu-container').css('width', totalWidth);
////////////////// タブの制御 //////////////////
$('.tab').on('click', function () {
///// タブの装飾
var index = $('.tab').index(this); // クリックしたタブ番号を取得
$('.tab').removeClass('current'); // タブ現在地クラスを削除し、
$(this).addClass('current'); // クリックしたタブにタブ現在地クラスを付与
///// コンテンツがスライドする動き
var carousel = function () { // 「carousel」という関数を定義
$('.menu-container').stop().animate({ // レール全体を左に動かす関数
left: index * -width // スライド数 × menu-box1枚分の幅を左に動かす
});
}
carousel(); // 「carousel」関数を実行
});
1~6行目ではmenu-containerの幅を計算し、CSSメソッドで代入しています。
15〜21行目ではcarouselという名前で関数を組んでスライドの動きを実行しています。
動かすのはanimate関数で、menu-boxの幅を代入した変数widthの長さ分左に動かすようにしています。
これでメニューがスライドするタブメニュー の完成です。
まとめ
メニューを表示させるアニメーションに凝らなければかなり簡単にできることが分かるかと思います。
この機会にコードをストックして使いまわせるようにしておきましょう!
また、当記事を読まれている方の中にはWeb制作初学者の方もいるかと思います。デザインやコーディングの基礎知識を学びたい方向けの記事を用意しているので是非見ていってください!
PENGIN無料コーディング課題
オススメ書籍紹介
オススメUdemy講座紹介