横スクロール比較表に「常時表示スクロールバー」を実装する方法:javascript

比較表のように列数が多い表をレスポンシブ対応させると、横スクロールが必須になります。
通常のブラウザのスクロールバーは、macOSのトラックパッド環境などでは「操作している時だけ表示」される仕様になっており、ユーザーが「まだ右にスクロールできる」ことに気づきにくいという問題があります。
この記事では、ネイティブのスクロールバーに頼らず、常に表示される自作のスクロールバーをHTML・CSS・JavaScriptだけで実装する方法を紹介します。
完成イメージ
- 表の上に、常に見えるスクロールバー(トラック+つまみ)を配置
- つまみをドラッグして表をスクロールできる
- スマホではスワイプ操作にもそのまま対応
- 表の1列目(項目名)はstickyで固定し、スクロールしても見出しが常に見える
HTMLの構成
自作スクロールバー用のdivと、表を囲うスクロールエリアを用意します。
<!-- 自作スクロールバー -->
<div class="custom-scrollbar">
<div class="custom-scrollbar-track" id="scrollTrack">
<div class="custom-scrollbar-thumb" id="scrollThumb"></div>
</div>
</div>
<!-- 本体テーブル -->
<div class="table-scroll" id="tableScroll">
<table class="compare-table" id="compareTable">
...
</table>
</div>
CSSのポイント:ネイティブのスクロールバーを消す
まず、表側のスクロールエリアからブラウザ標準のスクロールバーを見えなくします。overflow-x: auto自体は残すので、スクロール機能そのものは生きたままです。
.table-scroll{
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* 古いEdge */
-webkit-overflow-scrolling: touch; /* iOSの慣性スクロールを滑らかに */
}
.table-scroll::-webkit-scrollbar{
display: none; /* Chrome/Safari */
}
その代わりに、自作のトラックとつまみにデザインを当てます。
.custom-scrollbar-track{
position: relative;
height: 10px;
background: #e6edf4;
border-radius: 5px;
cursor: pointer;
}
.custom-scrollbar-thumb{
position: absolute;
top: 0;
left: 0;
height: 10px;
background: #5b8dc2;
border-radius: 5px;
cursor: grab;
}
JavaScriptのポイント①:つまみの幅と位置を計算する
つまみの幅は「表示中の幅 ÷ コンテンツ全体の幅」の比率から算出します。列数が多いほどこの比率は小さくなり、つまみも短くなります。
const MIN_THUMB_WIDTH = 30; // 最小幅
const MAX_THUMB_WIDTH = 120; // 最大幅
const THUMB_WIDTH_SCALE = 0.5; // 比率に掛ける倍率
function updateThumb() {
const trackWidth = scrollTrack.clientWidth;
const contentWidth = table.scrollWidth;
const visibleWidth = tableScroll.clientWidth;
const ratio = visibleWidth / contentWidth;
const rawWidth = trackWidth * ratio * THUMB_WIDTH_SCALE;
const thumbWidth = Math.min(Math.max(rawWidth, MIN_THUMB_WIDTH), MAX_THUMB_WIDTH);
const maxScrollLeft = table.scrollWidth - tableScroll.clientWidth;
const maxThumbLeft = trackWidth - thumbWidth;
const scrollRatio = maxScrollLeft > 0 ? tableScroll.scrollLeft / maxScrollLeft : 0;
scrollThumb.style.width = thumbWidth + 'px';
scrollThumb.style.left = (scrollRatio * maxThumbLeft) + 'px';
}
THUMB_WIDTH_SCALEを小さくするとつまみが短くなり、MIN_THUMB_WIDTH・MAX_THUMB_WIDTHでその上限・下限を制御できます。デザインに合わせて数値を調整するだけなので、CSSに直接widthを書くより柔軟に扱えます。
JavaScriptのポイント②:ドラッグとスワイプに対応する
つまみはmousedown/touchstartでドラッグ開始、mousemove/touchmoveで移動量に応じてscrollLeftを書き換え、mouseup/touchendで終了します。マウスとタッチの両方に同じロジックを使い回せるよう、startDrag / moveDrag / endDragという共通関数にまとめています。
scrollThumb.addEventListener('mousedown', e => startDrag(e.clientX));
document.addEventListener('mousemove', e => moveDrag(e.clientX));
document.addEventListener('mouseup', endDrag);
scrollThumb.addEventListener('touchstart', e => startDrag(e.touches[0].clientX), { passive: true });
document.addEventListener('touchmove', e => moveDrag(e.touches[0].clientX), { passive: true });
document.addEventListener('touchend', endDrag);
なお、表自体はoverflow-x: autoのままなので、スクロールバーが非表示でも、スマホでは表の中身を直接スワイプするだけで普通にスクロールできます。自作スクロールバーはあくまで「見た目のガイド兼、もう一つの操作手段」という位置づけです。
まとめ
- ネイティブのスクロールバーはOSやブラウザの設定に表示が左右されやすい
- ::-webkit-scrollbar { display: none } + 自作のトラック・つまみdivに置き換えることで、常時表示・自由なデザインが可能になる
- つまみの幅は固定値ではなく比率計算にしておくと、列数が変わっても自動で調整される
- マウスのドラッグとスマホのスワイプ、両方に対応させることでどの環境でも同じ操作感になる
完成コード
<!-- 自作スクロールバー(常時表示・本体テーブルと同期) -->
<div class="custom-scrollbar">
<div class="custom-scrollbar-track" id="scrollTrack">
<div class="custom-scrollbar-thumb" id="scrollThumb"></div>
</div>
</div>
<!-- 本体テーブル -->
<div class="table-scroll" id="tableScroll">
<table class="compare-table" id="compareTable">
<thead>
<tr>
<th class="row-label">製品名</th>
<th class="product-head">サンプル製品A</th>
<th class="product-head">サンプル製品B</th>
<th class="product-head">サンプル製品C</th>
<th class="product-head">サンプル製品D</th>
<th class="product-head">サンプル製品E</th>
</tr>
</thead>
<tbody>
<tr>
<td class="row-label">メーカー</td>
<td class="data-cell">ダミー株式会社</td>
<td class="data-cell">ダミー株式会社</td>
<td class="data-cell">サンプル工業</td>
<td class="data-cell">サンプル工業</td>
<td class="data-cell">テスト商事</td>
</tr>
</tbody>
</table>
</div>
ロジック部分はそのままコピーして使えるので、表の列数やデータに合わせて<table>部分だけ差し替えて利用できます。
シェア
ツイート


