タブ切り替え内にタブ切り替えを作る方法【HTML/CSS/JavaScript・サンプルコード付き】
タブ切り替え
2025.10.05
タブ切り替え内にタブ切り替えを作る方法
タブ切り替え内にタブ切り替えを作る方法を紹介します。このサンプルでは、外側のタブでコンテンツを切り替え、そのコンテンツ内にさらに別のタブ切り替えを配置します。情報を大きな項目と細かな項目に分けて整理したい場合などに利用できます。
HTMLで外側と内側それぞれのタブとコンテンツを作成し、CSSで選択中のタブや表示エリアをデザインします。JavaScriptではクリックされたタブに対応するコンテンツだけを表示し、外側と内側のタブをそれぞれ独立して切り替えられるようにします。
タブ名や表示内容は自由に変更できます。外側のタブで大きなカテゴリーを分け、その中をさらに複数の項目へ分けられるため、ひとつの表示エリアへ多くの情報を整理して配置できます。
コードについて
本記事のコードはサンプルコードです。ご利用前に必ず動作確認を行ってください。
免責事項
本コードの利用により発生した損害について、当サイトは一切の責任を負いません。
デモ
タブ切り替え内にタブ切り替えを表示
外側のタブを切り替え、その中にあるタブも個別に切り替えられます。
サービスについての情報を表示しています。
提供しているサービスの詳しい内容を表示します。
サービスを利用するときの方法を表示します。
「サービス」タブ内の「サービス内容」「利用方法」をクリックすると、内側のコンテンツも個別に切り替えられます。
コードをコピーして使おう!
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>タブ切り替え内にタブ切り替えを作る</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- タブ切り替え全体 -->
<div class="nested-tab-switch_wrap">
<!-- タイトルと説明文 -->
<div class="nested-tab-switch_title">
タブ切り替え内にタブ切り替えを表示
</div>
<p class="nested-tab-switch_text">
外側のタブを切り替え、その中にあるタブも個別に切り替えられます。
</p>
<!-- 外側のタブ -->
<div class="nested-tab-switch_tabs" role="tablist" aria-label="外側のタブ">
<button class="nested-tab-switch_tab is-active" type="button" data-outer-tab="service">
サービス
</button>
<button class="nested-tab-switch_tab" type="button" data-outer-tab="price">
料金
</button>
</div>
<!-- 「サービス」タブの表示内容 -->
<div class="nested-tab-switch_panel is-active" data-outer-panel="service">
<p class="nested-tab-switch_panel-text">
サービスについての情報を表示しています。
</p>
<!-- 外側のタブ内に配置する内側のタブ切り替え -->
<div class="nested-tab-switch_inner">
<!-- 内側のタブ -->
<div class="nested-tab-switch_inner-tabs">
<button class="nested-tab-switch_inner-tab is-active" type="button" data-inner-tab="content">
サービス内容
</button>
<button class="nested-tab-switch_inner-tab" type="button" data-inner-tab="use">
利用方法
</button>
</div>
<!-- 「サービス内容」タブの表示内容 -->
<div class="nested-tab-switch_inner-panel is-active" data-inner-panel="content">
提供しているサービスの詳しい内容を表示します。
</div>
<!-- 「利用方法」タブの表示内容 -->
<div class="nested-tab-switch_inner-panel" data-inner-panel="use">
サービスを利用するときの方法を表示します。
</div>
</div>
</div>
<!-- 「料金」タブの表示内容 -->
<div class="nested-tab-switch_panel" data-outer-panel="price">
<p class="nested-tab-switch_panel-text">
料金についての情報を表示しています。
</p>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
/* タブ切り替え全体 */
.nested-tab-switch_wrap,
.nested-tab-switch_wrap *{
box-sizing:border-box;
}
.nested-tab-switch_wrap{
width:100%;
max-width:700px; /* 全体の最大横幅。大きくすると横に広がる */
margin:40px auto; /* 上下の余白。40pxを大きくすると上下の間隔が広がる */
padding:32px; /* 内側の余白。大きくすると内容との間隔が広がる */
border:1px solid #e5e7eb; /* 全体の枠線。1pxで太さ、#e5e7ebで色を変更できる */
border-radius:18px; /* 全体の角丸。大きくすると丸みが強くなる */
background:#f8fafc; /* 全体の背景色 */
color:#111827; /* 基本の文字色 */
font-family:sans-serif;
}
/* タイトル */
.nested-tab-switch_title{
margin:0 0 12px; /* タイトル下の余白。12pxを大きくすると説明文との間隔が広がる */
font-size:22px; /* タイトルの文字サイズ */
font-weight:700;
line-height:1.5; /* タイトルの行間。大きくすると行間が広がる */
text-align:center;
}
/* 説明文 */
.nested-tab-switch_text{
margin:0 0 24px; /* 説明文下の余白。24pxを大きくするとタブとの間隔が広がる */
color:#4b5563; /* 説明文の文字色 */
font-size:16px; /* 説明文の文字サイズ */
line-height:1.8; /* 説明文の行間。大きくすると行間が広がる */
text-align:center;
}
/* 外側のタブ一覧 */
.nested-tab-switch_tabs{
display:flex;
border-bottom:1px solid #d1d5db; /* タブ一覧下部の枠線 */
}
/* 外側のタブ */
.nested-tab-switch_tab{
flex:1;
min-height:52px; /* タブの最小高さ。大きくするとタブが高くなる */
padding:14px; /* タブ内側の余白。大きくするとタブが広がる */
border:1px solid #d1d5db; /* タブの枠線 */
border-bottom:none;
border-radius:12px 12px 0 0; /* タブ上部の角丸。大きくすると丸みが強くなる */
background:#ffffff; /* 通常時のタブ背景色 */
color:#111827; /* 通常時のタブ文字色 */
font:inherit;
font-size:16px; /* タブの文字サイズ */
font-weight:700;
cursor:pointer;
transition:
background-color .3s ease, /* 背景色の切り替え時間。大きくするとゆっくり変化する */
color .3s ease; /* 文字色の切り替え時間。大きくするとゆっくり変化する */
}
/* 外側のタブ同士の間隔 */
.nested-tab-switch_tab + .nested-tab-switch_tab{
margin-left:6px; /* タブ同士の間隔。大きくすると間隔が広がる */
}
/* 選択中の外側タブ */
.nested-tab-switch_tab.is-active{
background:#0b6bff; /* 選択中のタブ背景色 */
color:#ffffff; /* 選択中のタブ文字色 */
}
/* 外側のコンテンツ */
.nested-tab-switch_panel{
display:none;
padding:24px; /* コンテンツ内側の余白。大きくすると内容との間隔が広がる */
border:1px solid #d1d5db; /* コンテンツの枠線 */
border-top:none;
background:#ffffff; /* コンテンツの背景色 */
}
/* 選択中の外側コンテンツ */
.nested-tab-switch_panel.is-active{
display:block;
}
/* 外側コンテンツの説明文 */
.nested-tab-switch_panel-text{
margin:0 0 20px; /* 説明文下の余白。大きくすると内側タブとの間隔が広がる */
color:#4b5563; /* 説明文の文字色 */
font-size:16px; /* 説明文の文字サイズ */
line-height:1.8; /* 説明文の行間 */
}
/* 内側のタブ切り替え */
.nested-tab-switch_inner{
padding:20px; /* 内側タブ切り替えの余白。大きくすると内容との間隔が広がる */
border-radius:14px; /* 内側タブ切り替えの角丸 */
background:#f8fafc; /* 内側タブ切り替えの背景色 */
}
/* 内側のタブ一覧 */
.nested-tab-switch_inner-tabs{
display:flex;
gap:8px; /* 内側タブ同士の間隔。大きくすると間隔が広がる */
margin-bottom:16px; /* タブとコンテンツの間隔。大きくすると間隔が広がる */
}
/* 内側のタブ */
.nested-tab-switch_inner-tab{
flex:1;
padding:11px 12px; /* 内側タブの上下・左右の余白 */
border:1px solid #bfdbfe; /* 内側タブの枠線 */
border-radius:8px; /* 内側タブの角丸 */
background:#ffffff; /* 内側タブの背景色 */
color:#1e40af; /* 内側タブの文字色 */
font:inherit;
font-size:15px; /* 内側タブの文字サイズ */
font-weight:700;
cursor:pointer;
}
/* 選択中の内側タブ */
.nested-tab-switch_inner-tab.is-active{
border-color:#2563eb; /* 選択中の内側タブの枠線色 */
background:#dbeafe; /* 選択中の内側タブの背景色 */
}
/* 内側のコンテンツ */
.nested-tab-switch_inner-panel{
display:none;
padding:18px; /* 内側コンテンツの余白。大きくすると内容との間隔が広がる */
border:1px solid #dbeafe; /* 内側コンテンツの枠線 */
border-radius:10px; /* 内側コンテンツの角丸 */
background:#ffffff; /* 内側コンテンツの背景色 */
color:#4b5563; /* 内側コンテンツの文字色 */
font-size:15px; /* 内側コンテンツの文字サイズ */
line-height:1.8; /* 内側コンテンツの行間 */
}
/* 選択中の内側コンテンツ */
.nested-tab-switch_inner-panel.is-active{
display:block;
}
/* 画面幅640px以下 */
@media(max-width:640px){
.nested-tab-switch_wrap{
padding:24px 18px; /* スマートフォン時の全体の上下・左右余白 */
}
.nested-tab-switch_tab{
min-height:48px; /* スマートフォン時のタブの最小高さ */
padding:12px 8px; /* スマートフォン時のタブ内側の余白 */
font-size:14px; /* スマートフォン時のタブ文字サイズ */
}
.nested-tab-switch_tab + .nested-tab-switch_tab{
margin-left:4px; /* スマートフォン時の外側タブ同士の間隔 */
}
.nested-tab-switch_panel{
padding:20px 16px; /* スマートフォン時のコンテンツ内側の余白 */
}
.nested-tab-switch_inner{
padding:16px; /* スマートフォン時の内側タブ切り替えの余白 */
}
.nested-tab-switch_inner-tab{
padding:10px 8px; /* スマートフォン時の内側タブの余白 */
font-size:14px; /* スマートフォン時の内側タブ文字サイズ */
}
}
// 外側のタブとコンテンツを取得
const nestedOuterTabs =
document.querySelectorAll(".nested-tab-switch_tab");
const nestedOuterPanels =
document.querySelectorAll(".nested-tab-switch_panel");
// 外側のタブをクリックしたときに表示内容を切り替える
nestedOuterTabs.forEach(function(tab){
tab.addEventListener("click",function(){
const target = tab.dataset.outerTab;
// すべての外側タブから選択状態を外す
nestedOuterTabs.forEach(function(item){
item.classList.remove("is-active");
});
// すべての外側コンテンツを非表示にする
nestedOuterPanels.forEach(function(panel){
panel.classList.remove("is-active");
});
// クリックしたタブと対応するコンテンツを表示する
tab.classList.add("is-active");
document
.querySelector('[data-outer-panel="' + target + '"]')
.classList.add("is-active");
});
});
// 内側のタブとコンテンツを取得
const nestedInnerTabs =
document.querySelectorAll(".nested-tab-switch_inner-tab");
const nestedInnerPanels =
document.querySelectorAll(".nested-tab-switch_inner-panel");
// 内側のタブをクリックしたときに表示内容を切り替える
nestedInnerTabs.forEach(function(tab){
tab.addEventListener("click",function(){
const target = tab.dataset.innerTab;
// すべての内側タブから選択状態を外す
nestedInnerTabs.forEach(function(item){
item.classList.remove("is-active");
});
// すべての内側コンテンツを非表示にする
nestedInnerPanels.forEach(function(panel){
panel.classList.remove("is-active");
});
// クリックしたタブと対応するコンテンツを表示する
tab.classList.add("is-active");
document
.querySelector('[data-inner-panel="' + target + '"]')
.classList.add("is-active");
});
});
コードをコピーする場合
使用するコードのタブを選び、「このタブをコピー」をクリックすると、現在表示中のコードをコピーできます。
サンプル一式を取得する場合
HTML・CSSなどすべてのファイルが必要な場合は、「サンプル一式をダウンロード」をクリックしてください。
ファイル構成と設置方法
このサンプルはHTML・CSS・JavaScriptの3ファイルで構成されています。HTMLで外側のタブ切り替えを作成し、「サービス」の表示内容の中へさらに内側のタブ切り替えを配置します。CSSで外側と内側それぞれのタブやコンテンツの見た目を整え、JavaScriptでクリックしたタブに対応する内容へ切り替えます。
sample/
├── index.html
├── style.css
└── script.js
各ファイルの役割
index.htmlは、タブ切り替え全体の構造を作るファイルです。外側には「サービス」「料金」のタブがあり、「サービス」のコンテンツ内には「サービス内容」「利用方法」の内側タブを配置しています。タブ名や各タブに表示する文章を変更したい場合は、このファイルを編集します。
style.cssは、外側と内側のタブ切り替えのデザインを設定するファイルです。全体の横幅や余白、枠線、角丸、背景色のほか、通常時と選択中のタブの色、コンテンツ部分の余白や文字サイズなどを変更できます。
script.jsは、タブをクリックしたときの切り替え処理を行うファイルです。外側では「サービス」と「料金」を切り替え、内側では「サービス内容」と「利用方法」を切り替えます。それぞれのタブに設定したdata属性を使って、対応するコンテンツへis-activeクラスを付け替えています。
設置方法・使い方
sampleフォルダを作成し、その中へindex.html、style.css、script.jsを同じ階層で配置します。index.htmlからstyle.cssとscript.jsを読み込んでいるため、ファイル名を変更せずに配置してください。
3つのファイルを配置したあと、sample/index.htmlをブラウザで開きます。「サービス」と「料金」をクリックすると外側の表示内容が切り替わります。「サービス」を表示している状態では、その中にある「サービス内容」と「利用方法」をクリックして内側の表示内容も切り替えられます。
カスタマイズできる内容
index.htmlでは、外側の「サービス」「料金」、内側の「サービス内容」「利用方法」というタブ名や、それぞれに対応する表示内容を変更できます。外側のタブとコンテンツはdata-outer-tabとdata-outer-panel、内側はdata-inner-tabとdata-inner-panelの値を対応させて使用します。
style.cssでは、全体の最大横幅や余白、背景色、枠線、角丸を変更できます。また、外側タブの通常時と選択中の背景色・文字色、内側タブの背景色・枠線色・文字色、各コンテンツの余白や文字サイズも変更できます。
script.jsでは、外側と内側のタブをクリックしたときに付け替えるis-activeクラスによって表示状態を管理しています。HTMLのdata属性と対応するコンテンツを取得し、選択した内容だけを表示する仕組みになっています。
数値調整
style.cssのmax-width:700px;はタブ切り替え全体の最大横幅です。値を大きくすると全体が横へ広がり、小さくするとコンパクトになります。
margin:40px auto;の40pxは全体の上下余白、padding:32px;は全体の内側余白です。値を大きくすると周囲や内容との間隔が広がります。border-radius:18px;は全体の角丸で、値を大きくすると丸みが強くなります。
外側のタブに設定したmin-height:52px;は最小の高さ、padding:14px;は内側余白です。margin-left:6px;は隣り合う外側タブの間隔を指定しています。transitionの.3sは背景色と文字色が切り替わる時間で、値を大きくするとゆっくり、小さくすると速く変化します。
外側コンテンツのpadding:24px;は内側余白です。内側のタブ切り替えではpadding:20px;、border-radius:14px;を設定しています。内側タブのgap:8px;はタブ同士の間隔、margin-bottom:16px;はタブとコンテンツの間隔です。内側コンテンツのpadding:18px;とborder-radius:10px;を変更すると、コンテンツ内の余白と角丸を調整できます。
レスポンシブ対応
このサンプルはレスポンシブ対応です。style.cssで全体にwidth:100%とmax-width:700pxを設定し、画面幅に合わせて縮小できるようにしています。
画面幅640px以下ではメディアクエリを使用し、全体の余白を24px 18px、外側タブの最小高さを48px、文字サイズを14pxへ変更します。外側コンテンツや内側タブ切り替えの余白、内側タブの文字サイズも小さくなるため、スマートフォン・タブレット・PCで利用できます。
タブ切り替え内にタブ切り替えを作る方法【HTML/CSS/JavaScript・サンプルコード付き】
コメント