コピペで完結!タブ切替 #07【スクロール可能タブ(ドラッグ/ホイール)】
html/css/js
2025.10.05
スクロール可能タブ(ドラッグ/ホイール)
タブが多く横幅に収まらない場合でも、ホイール・ドラッグ・スワイプでスクロールできるタブUIです。
PCではマウスホイールやドラッグ、スマホではスワイプ操作でタブを切り替えやすくできます。
コードについて
本記事のコードは AI(ChatGPT)による生成をベースに作成・調整しています。ご利用の環境でテストの上ご使用ください。
免責
本コードの利用に伴う不具合・損害について、当サイトは責任を負いません。自己責任にてご利用ください。
デモ
下のタブは全部で12個あります。画面幅に収まりきらないので、横にスクロールして確認してください。
タブ1の内容です
タブ2の内容です
タブ3の内容です
タブ4の内容です
タブ5の内容です
タブ6の内容です
タブ7の内容です
タブ8の内容です
タブ9の内容です
タブ10の内容です
タブ11の内容です
タブ12の内容です
コードをコピーして使おう!
/* =========================================
スクロール可能タブのCSS(学習用に全文コメント)
-----------------------------------------
・横幅に収まらないほどタブを大きく(min-width)
・横スクロールを有効化(overflow-x:auto)
・選択中タブは色で強調
・必要最小限の見た目に限定(衝突回避のため #demo-section にスコープ)
========================================= */
/* ルート:この記事のデモ領域に限定するためのラッパー */
#demo-section.demo-wrap{
max-width:960px; /* 記事幅に合わせる横幅上限 */
margin:0 auto; /* 中央寄せ */
padding:0 16px; /* 左右余白 */
font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Noto Sans JP","Yu Gothic UI",sans-serif; /* 可読フォント */
color:#0f172a; /* 基本文字色 */
--ink:#0f172a; /* カラートークン:文字色 */
--accent:#0b6bff; /* カラートークン:強調色(アクティブ時の色) */
--border:#e5e7eb; /* カラートークン:枠線色 */
}
/* タブバー(横スクロールできる行) */
#demo-section .scroll-tabs{
display:flex; /* 子要素(ボタン)を横並び */
gap:8px; /* タブ間のすき間 */
overflow-x:auto; /* 横方向へスクロール可能にする */
-webkit-overflow-scrolling:touch;/* iOSの慣性スクロールを有効化 */
scrollbar-width:none; /* Firefoxのスクロールバー非表示 */
}
/* Chrome/Safari 等のスクロールバーを非表示 */
#demo-section .scroll-tabs::-webkit-scrollbar{ display:none; }
/* タブボタンの共通見た目 */
#demo-section .scroll-tabs button{
flex:0 0 auto; /* 伸縮せず等幅で横並び維持 */
min-width:180px; /* 1つを大きくする(スクロール必須化) */
appearance:none; /* OS/ブラウザの標準装飾を無効化 */
border:1px solid var(--border); /* 薄い枠線 */
background:#fff; /* 背景白 */
border-radius:8px; /* 角丸 */
padding:14px 20px; /* 中の余白(押しやすく) */
font-size:15px; /* 文字サイズ */
cursor:pointer; /* ポインタにする */
color:var(--ink); /* 文字色 */
transition:background .25s,color .25s,border-color .25s; /* 滑らかに色変化 */
}
/* 選択中タブの見た目(aria-selected=true をJSが付与) */
#demo-section .scroll-tabs button[aria-selected="true"]{
background:var(--accent); /* 背景を強調色に */
color:#fff; /* 文字は白 */
border-color:var(--accent); /* 枠線も強調色 */
font-weight:600; /* 少し強調 */
}
/* タブに対応する内容パネル */
#demo-section .panel{
border:1px solid var(--border); /* 枠線 */
border-radius:10px; /* 角丸 */
padding:16px; /* 余白 */
margin-top:16px; /* タブとの余白 */
background:#fff; /* 背景白 */
}
/* 備考:
・パネルの表示/非表示は HTML の hidden 属性を JS で付け替えます。
・CSS は純粋に見た目のみを定義しています。 */
<!-- =========================================
スクロール可能タブのHTML(学習用に全文コメント)
-----------------------------------------
・role="tablist" / role="tab" / role="tabpanel" を付与してアクセシブルに
・各タブの aria-controls は対応するパネルの id を参照
・初期表示はタブ1だけ aria-selected="true"、パネル1だけ hidden を外す
・デモ体感のためタブは 12 個(横幅を超えてスクロールさせる)
========================================= -->
<section id="demo-section" class="demo-wrap">
<!-- タブバー(横スクロール可能) -->
<div class="scroll-tabs" role="tablist" aria-label="コンテンツの切り替えタブ">
<button role="tab" aria-selected="true" aria-controls="panel1">タブ1</button>
<button role="tab" aria-selected="false" aria-controls="panel2">タブ2</button>
<button role="tab" aria-selected="false" aria-controls="panel3">タブ3</button>
<button role="tab" aria-selected="false" aria-controls="panel4">タブ4</button>
<button role="tab" aria-selected="false" aria-controls="panel5">タブ5</button>
<button role="tab" aria-selected="false" aria-controls="panel6">タブ6</button>
<button role="tab" aria-selected="false" aria-controls="panel7">タブ7</button>
<button role="tab" aria-selected="false" aria-controls="panel8">タブ8</button>
<button role="tab" aria-selected="false" aria-controls="panel9">タブ9</button>
<button role="tab" aria-selected="false" aria-controls="panel10">タブ10</button>
<button role="tab" aria-selected="false" aria-controls="panel11">タブ11</button>
<button role="tab" aria-selected="false" aria-controls="panel12">タブ12</button>
</div>
<!-- 各パネル(初期は panel1 のみ表示。以外は hidden) -->
<div id="panel1" class="panel" role="tabpanel" aria-labelledby="tab1">タブ1の内容です</div>
<div id="panel2" class="panel" role="tabpanel" hidden>タブ2の内容です</div>
<div id="panel3" class="panel" role="tabpanel" hidden>タブ3の内容です</div>
<div id="panel4" class="panel" role="tabpanel" hidden>タブ4の内容です</div>
<div id="panel5" class="panel" role="tabpanel" hidden>タブ5の内容です</div>
<div id="panel6" class="panel" role="tabpanel" hidden>タブ6の内容です</div>
<div id="panel7" class="panel" role="tabpanel" hidden>タブ7の内容です</div>
<div id="panel8" class="panel" role="tabpanel" hidden>タブ8の内容です</div>
<div id="panel9" class="panel" role="tabpanel" hidden>タブ9の内容です</div>
<div id="panel10" class="panel" role="tabpanel" hidden>タブ10の内容です</div>
<div id="panel11" class="panel" role="tabpanel" hidden>タブ11の内容です</div>
<div id="panel12" class="panel" role="tabpanel" hidden>タブ12の内容です</div>
</section>
/* =========================================
スクロール可能タブのJS(学習用に全文コメント)
-----------------------------------------
・クリックで aria-selected を切替
・対応パネルの hidden を付け外し
・タブバーをドラッグで横スクロール可能に
========================================= */
(function(){
/* ルート要素(このデモのセクション)を取得 */
const root = document.querySelector('#demo-section');
if(!root) return; /* 念のためガード */
/* すべてのタブボタン(role="tab")とパネル(role="tabpanel")を取得 */
const tabs = root.querySelectorAll('[role="tab"]');
const panels = root.querySelectorAll('[role="tabpanel"]');
/* タブの有効化関数:引数で受けたタブを選択状態にする */
function activateTab(newTab){
tabs.forEach(tab => {
const selected = tab === newTab; /* 今回クリックしたタブか? */
tab.setAttribute('aria-selected', selected); /* 選択状態を反映 */
const panel = root.querySelector('#' + tab.getAttribute('aria-controls')); /* 紐づくパネルを取得 */
if(panel) panel.hidden = !selected; /* 選択時だけ表示(hidden を外す) */
});
}
/* クリックでタブ切替 */
tabs.forEach(tab => {
tab.addEventListener('click', () => activateTab(tab));
});
/* ------- ここから:ドラッグで横スクロール ------- */
const tabList = root.querySelector('.scroll-tabs'); /* 横スクロール領域 */
let isDown = false; /* マウス押下中フラグ */
let startX = 0; /* 押下開始時のX座標 */
let scrollLeft = 0; /* 押下開始時のスクロール量 */
/* マウス押下時:ドラッグ開始 */
tabList.addEventListener('mousedown', (e) => {
isDown = true; /* ドラッグ開始 */
startX = e.pageX - tabList.offsetLeft; /* クリック位置(要素左端からの距離) */
scrollLeft = tabList.scrollLeft; /* 現在のスクロール量を記録 */
});
/* マウスが外れた/離したらドラッグ終了 */
tabList.addEventListener('mouseleave', () => { isDown = false; });
tabList.addEventListener('mouseup', () => { isDown = false; });
/* マウス移動:押下中だけスクロール量を更新 */
tabList.addEventListener('mousemove', (e) => {
if(!isDown) return; /* 押していないときは無視 */
e.preventDefault(); /* テキスト選択などの既定動作を抑制 */
const x = e.pageX - tabList.offsetLeft; /* 現在位置 */
const walk = (x - startX) * 1; /* 移動量(係数で速度調整可) */
tabList.scrollLeft = scrollLeft - walk; /* 左右にスクロール */
});
/* ------- ここまで:ドラッグで横スクロール ------- */
})();
コピペで完結!タブ切替 #07【スクロール可能タブ(ドラッグ/ホイール)】
コメント