HTMLとCSSだけでタブのメニューをかんたんに作る方法をご紹介します。
このタブメニューは、画面を遷移しないで表示内容のコンテンツを切り替えます。JavaScriptは使いません。
これは、タブを切り替えるときに、Webサーバーにリクエストを送信しないので、Webページのパフォーマンスを上げることができます。
まずは完成したサンプルから。
See the Pen tab sample 2 by tadtadya (@tadtadya) on CodePen.
これをHTMLとCSSだけで作ります。少しづつ実装するので、とてもかんたんで分かりやすいと思います。
JavaScriptを使わずにタブを表示
サンプルをつくっていく前に、JavaScriptを使わないメリットから。
HTMLとCSSだけでタブをつくるとWebサーバにリクエストする回数を最小限にできます。そのしくみは、
- ブラウザからhttpリクエスト。
- Webサーバーから、すべてのタブコンテンツの情報を付けたHTMLをレスポンス。
- CSSで、初期表示するタブコンテンツを表示し、そのほかのタブコンテンツはかくす。
- タブの切り替えをCSSでコントロールする。
これはタブの切り替えでページ遷移をしません。
また、コンテンツをリクエストで再取得しないとしても、JavaScriptのDOM操作がいらないので、その分だけブラウザの負荷を軽くできます。
あらかじめ、CSSですべての操作のスタイルが定義されているので、CSSOMの再展開がない。
(CSSOMはCSSのDOMみたいなもの。)
(JavaScriptのDOM, CSSOM操作の分がいらない。)
また、HTML, CSSに任せることでJavaScriptのコードを少なくできる。
CSSだけでタブの動作を行なうかんたんな作り方
少しだけHTMLも関係していますが、タブ動作のほぼすべてはCSSだけで行ないます。
CSSの記述はかんたんです。ソースコードのサンプルを少しづつ実装しながら見ていきましょう。
ソースコードサンプルのボックスにタップやマウスを合わせると、右上にコピーボタンが出ます。
慣れないうちは、とりあえずコピー&ペーストで、自分で動かすところから始めると良いです。
タブのコンテンツ切り替えのソースコードサンプル
まずは、HTMLとCSSを見てください。
<input type="radio" id="tab1" class="radio" name="tab" checked="checked" ><label class="tab-title title1" for="tab1">tab1</label>
<input type="radio" id="tab2" class="radio" name="tab" ><label class="tab-title title2" for="tab2">tab2</label>
<input type="radio" id="tab3" class="radio" name="tab" ><label class="tab-title title3" for="tab3">tab3</label>
<div class="tab-body">
<div class="body1">body1 body1 body1</div>
<div class="body2">body2 body2 body2</div>
<div class="body3">body3 body3 body3</div>
</div>
.title1, .body1 {
border: 2px solid rgb(127, 194, 239);
}
.title2, .body2 {
border: 2px solid rgb(214, 133, 176);
}
.title3, .body3 {
border: 2px solid rgb(219, 225, 89);
}
.tab-title {
border-bottom: none;
padding: .3em .5em;
border-radius: .3em .3em 0 0;
text-align: center;
display: table;
}
.tab-body > div {
width: 100%;
height: 200px;
border-radius: 0 .3em .3em .3em;
padding: 1em;
}
HTMLは、タブをラジオボタンとラベルで作って、対応する表示エリアを作りました。それを3個ずつ用意しています。
まだタブの機能はありません。
CSSは、ラジオボタンのラベルにタグタイトルっぽい見た目をつくりました。また、表示エリアに枠をつけています。
この状態でHTMLを動かしてみましょう。
すべてのコンテンツが見えています。タブの配置もできていません。
また、タブを押してもラジオボタンのチェックが動くだけで何も起きません。
ラジオボタンが見えてしまっているのもダメです。まだまだタブとしてできていません。
ここでHTMLとCSSにさらに追加して、タブの配置を完成させます。タブの機能はまだです。もう少し待ってください。
<div class="container">
<!-- さっきのHTML -->
</div>
/* radio non-display */
.container .radio {
display: none;
}
/* tabs position */
.container {
display: flex;
flex-wrap: wrap;
position: relative;
}
.container::after {
content: "";
width: 100%;
}
.container .tab-title {
position: relative;
border-bottom: 2px solid transparent;
top: 2px;
left: 2px;
}
.container .tab-title:hover {
cursor: pointer;
}
.container .tab-body {
order: 1;
}
HTML&CSSの全文はこちら。
<div class="container">
<input type="radio" id="tab1" class="radio" name="tab" checked="checked"><label class="tab-title title1" for="tab1">tab1</label>
<input type="radio" id="tab2" class="radio" name="tab" ><label class="tab-title title2" for="tab2">tab2</label>
<input type="radio" id="tab3" class="radio" name="tab" ><label class="tab-title title3" for="tab3">tab3</label>
<div class="tab-body">
<div class="body1">body1 body1 body1</div>
<div class="body2">body2 body2 body2</div>
<div class="body3">body3 body3 body3</div>
</div>
</div>
.title1, .body1 {
border: 2px solid rgb(127, 194, 239);
}
.title2, .body2 {
border: 2px solid rgb(214, 133, 176);
}
.title3, .body3 {
border: 2px solid rgb(219, 225, 89);
}
.tab-title {
border-bottom: none;
padding: .3em .5em;
border-radius: .3em .3em 0 0;
text-align: center;
display: table;
}
.tab-body > div {
width: 100%;
height: 200px;
border-radius: 0 .3em .3em .3em;
padding: 1em;
}
/* radio non-display */
.container .radio {
display: none;
}
/* tabs position */
.container {
display: flex;
flex-wrap: wrap;
position: relative;
}
.container::after {
content: "";
width: 100%;
}
.container .tab-title {
position: relative;
border-bottom: 2px solid transparent;
top: 2px;
left: 2px;
}
.container .tab-title:hover {
cursor: pointer;
}
.container .tab-body {
order: 1;
}
さっきのHTMLにcontainerクラスの親を追加しました。これはflexのコンテナになります。
CSSの説明の前にHTMLを実行してみましょう。
一気にタブらしくなりました。どうしてここまで完成度が高くなったのでしょう。
CSSをひとつずつ解説します。
タブの配置のCSS
- ラジオボタン非表示。
- タブラベル、表示エリアをflexで折り返しありのヨコ並び。
- flexコンテナの疑似要素(::after)でタブラベルと表示エリアの境界ボックスを作成。
- flexボックス(タブコンテンツ本体)にorder: 1を指定。(折り返しのあとにボックスが来るように)
- タブラベルの下の枠線を透明に。
- タブラベルの位置を、透明枠線でコンテンツ本体の枠線にかぶせる。
- タブラベルにカーソルを合わせたときポインターの形状に。
いくつか???というところがあるかもしれません。この処理のポイントです。
flexの使い方が分からないと意味不明です。
- 表示エリアにorder:1を指定して、エリアがタブラベル+境界ボックスの下に来るように並び替える。
- 境界ボックス(::after)のwidth:100%で、必ずflexの折り返しがある。-> 最初の表示エリアで必ず折り返す
- 表示エリアはwidth:100%なので、ひとつずつ折り返す。-> ヨコ並びにならない。-> タテ並びになる。
- タブラベルの下枠線の透明...は、タブ表示エリアの一体感を表すための細かい調整
境界ボックスに色をつけるとわかりやすいです。
(::afterにheight: 10pxを追加して色をつけてます。)
ここまでで、タブの切り替え機能以外は完成しました。
表示エリアがぜんぶ表示されるのはこのままでいいです。タブの切り替え機能で表示/非表示をコントロールするので。
さいごにタブに切り替え機能を追加します。処理はすべてCSSに追加します。
/* tab's body init */
.add-control .tab-body > div {
display: none;
}
/* selected tab's color change */
.add-control .radio:checked + .tab-title {
color: #fff;
border-bottom-color: #fff;
}
.add-control #tab1:checked ~ .title1 {
background: rgba(127, 194, 239, .5);
}
.add-control #tab2:checked ~ .title2 {
background: rgba(214, 133, 176, .5);
}
.add-control #tab3:checked ~ .title3 {
background: rgba(219, 225, 89, .5);
}
/* tabs control */
.add-control #tab1:checked ~ .tab-body > .body1 {
display: block;
}
.add-control #tab2:checked ~ .tab-body > .body2 {
display: block;
}
.add-control #tab3:checked ~ .tab-body > .body3 {
display: block;
}
CSSセレクタ(~): 同じ階層を見る
要素と同じ階層にある要素に対してスタイルを適用させるためのセレクタ。
間接セレクタ。兄弟(姉妹)セレクタとも言う。
<div class="parent">親
<div class="child-1">子</div>
<div class="child-2">子</div>
<div class="child-3">子
<div class="child-child">孫</div>
</div>
</div>
CSSサンプル
// child-1, child-2, child-3が対象
.child-1 ~ div {
color: red;
}
タブをコントロールするクラス(add-control)を作りました。
その処理は、
- 表示エリアをすべてかくす。
- 選択したタブラベルの背景色・文字色変更。
- 選択したタブラベルの下枠線を白色にして、タブラベルと表示エリアの境界をなくす -> ラベルとエリアの一体化。
- 選択したタブに対応した表示エリアだけを表示。
です。このクラスをHTMLの一番上のdivのクラスにadd-controlを追加します。
HTMLとCSSの全文です。
<div class="container add-control">
<input type="radio" id="tab1" class="radio" name="tab" checked="checked"><label class="tab-title title1" for="tab1">tab1</label>
<input type="radio" id="tab2" class="radio" name="tab" ><label class="tab-title title2" for="tab2">tab2</label>
<input type="radio" id="tab3" class="radio" name="tab" ><label class="tab-title title3" for="tab3">tab3</label>
<div class="tab-body">
<div class="body1">body1 body1 body1</div>
<div class="body2">body2 body2 body2</div>
<div class="body3">body3 body3 body3</div>
</div>
</div>
.title1, .body1 {
border: 2px solid rgb(127, 194, 239);
}
.title2, .body2 {
border: 2px solid rgb(214, 133, 176);
}
.title3, .body3 {
border: 2px solid rgb(219, 225, 89);
}
.tab-title {
border-bottom: none;
padding: .3em .5em;
border-radius: .3em .3em 0 0;
text-align: center;
display: table;
}
.tab-body > div {
width: 100%;
height: 200px;
border-radius: 0 .3em .3em .3em;
padding: 1em;
}
/* radio non-display */
.container .radio {
display: none;
}
/* tabs position */
.container {
display: flex;
flex-wrap: wrap;
position: relative;
}
.container::after {
content: "";
width: 100%;
}
.container .tab-title {
position: relative;
border-bottom: 2px solid transparent;
top: 2px;
left: 2px;
}
.container .tab-title:hover {
cursor: pointer;
}
.container .tab-body {
order: 1;
}
/* tab's body init */
.add-control .tab-body > div {
display: none;
}
/* selected tab's color change */
.add-control .radio:checked + .tab-title {
color: #fff;
border-bottom-color: #fff;
}
.add-control #tab1:checked ~ .title1 {
background: rgba(127, 194, 239, .5);
}
.add-control #tab2:checked ~ .title2 {
background: rgba(214, 133, 176, .5);
}
.add-control #tab3:checked ~ .title3 {
background: rgba(219, 225, 89, .5);
}
/* tabs control */
.add-control #tab1:checked ~ .tab-body > .body1 {
display: block;
}
.add-control #tab2:checked ~ .tab-body > .body2 {
display: block;
}
.add-control #tab3:checked ~ .tab-body > .body3 {
display: block;
}
HTMLとCSSだけでタブができました。
これまで順を追って作ってきたサンプルコードを整理すると、最初のCodePenのHTMLとCSSになります。
【解説】CSSのタブ切り替えのポイント(:checked 疑似クラス)
タブの切り替えにはCSSの:checked疑似クラスを使う
:checked疑似クラスは、HTMLのinput要素のchecked属性と連動しています。
HTMLのchecked属性は、input要素が選択済みになると、checked属性がつけられます。
サンプルコードでは、タブ1のinput要素に初期値としてchecked属性がついています。
<input type="radio" id="tab1" class="radio" name="tab" checked="checked" ><label class="tab-title title1" for="tab1">tab1</label>
【解説】HTML文のタブ切り替えのポイント(radioボタンを使用)
HTMLは、radioボタンをカスタマイズしてタブに使う
radioボタンを使うのは、タブはつねにひとつしか選択できないので、radioボタンの機能がタブの切り替えと同じだからです。
radioボタンをクリックすると、input要素のchecked属性が自動的に切り替わるので、これをタブの切り替えに使います。
radioボタンは内部の切り替え機能だけが必要なので、CSSのdisplay:noneでかくします。
そしてタブの表示は、radioボタンのとなりにあるテキスト部分(label要素)で表現します。
【解説】CSSのタブ切り替え
タブの切り替えのコントロールはCSSで行う
HTMLのradioボタンのchecked属性だけでタブの切り替えを行なうので、JavaScriptは必要ありません。
HTMLとCSSだけでタブの切り替えができるのは、そのコントロールをHTMLにある機能だけで実現しているからです。CSSはそれを見た目の面でカバーしているだけです。
CSSのタブの切り替えは、サンプルコードの後半部分だけです。(:checkedのところ)
タブの切り替え処理
- タブのすべての表示エリアをdisplay: noneでかくす。
- checked属性がついたタブラベルの色をアクティブな色に変える
- 各タブは、checked属性がついてアクティブになるときは、連動する表示エリアをdisplay: blockで表示
CSSの処理もシンプルです。
display: noneですべての表示エリアをかくしますが、Webページ表示するとき、tab1にchecked属性がつくので最初はタブ1が表示されます。
まとめ
- HTMLはradioボタンのchecked属性を利用する
- CSSは:checked疑似クラスを使う
HTMLとCSSだけでタブを表示・操作することは、見た目と動きでむずかしく感じますが、作り方はかんたんですね?
サンプルコードでは、デザインをシンプルにしています。もっとオシャレにしたい場合は、ラベルとコンテンツエリアのborder, color, backgroundあたりを変えれば劇的に変わります。
:checked疑似クラスはCSS3から実装された機能です。現在のブラウザは、ほぼ対応しているので気にすることはないですが、どうしても動かない場合の知識として押さえておきましょう。
今回ご紹介したタグ機能と同じ作り方で、JavaScriptを使わないその他の機能も実装できます。