以下では、DOMを直接操作して「画像スライダー / カルーセル」を実装する方法を、基礎からアクセシビリティ・パフォーマンス配慮まで段階的に解説します。フレームワークは使わず、HTML/CSS/JavaScriptの素の構成で進めます。
1. 基本設計の考え方
-
レイアウト:横一列に並べたスライドを
transform: translateX()で移動。GPU支援が効きやすく、スムーズ。 -
ナビゲーション:前後ボタン(Prev/Next)とインジケータ(ドット)。
-
アクセシビリティ:
role="region"、aria-roledescription="carousel",aria-label、各スライドにrole="group"と「現在位置/全体枚数」の読み上げ。ボタンにaria-controlsとラベル。 -
操作:クリック、キーボード(← → / Home / End)、タッチ/ドラッグ(スワイプ)。
-
パフォーマンス:
will-change: transform、画像はloading="lazy"。IntersectionObserverで近傍だけプリロード。 -
無限ループ:両端にクローンを置いてシームレスに巻き戻す。
-
オートプレイ:
setIntervalではなく、ユーザー操作で停止・再開できる制御と、フォーカス時/ホバー時は停止。
2. 最小HTML(意味付けとARIA)
3. 基本CSS(1画面に1枚、トランジション)
4. JavaScript(基本の移動・ボタン・ドット)
5. 無限ループの実装(発展)
「端で止まらず連続スクロール」したい場合は、先頭と末尾のスライドをクローンして前後に挿入し、トランジション完了時にインデックスを巻き戻す方式が扱いやすいです。
手順の要点のみ:
-
firstClone = firstSlide.cloneNode(true),lastClone = lastSlide.cloneNode(true)を作成し、trackの前後に挿入。 -
実インデックスを
1(最初のクローンの次)から始める。 -
transitionendイベントで、indexがクローン位置になったらトランジションを一時的に無効化し、本来の対応スライドへ瞬間移動してから再度有効化。
この方式により、視覚的には無限に回って見えます。
6. アクセシビリティの要点
-
ロールとラベル:カルーセル全体に
role="region"とaria-roledescription="carousel"。視覚的ラベルはaria-label。 -
現在位置の通知:
aria-live="polite"を必要時のみ短時間有効化。頻繁なライブ更新は避ける。 -
フォーカス制御:ボタンやドットは
tabindex=0のデフォルトで操作可能。スライド内部リンクが多い場合はタブ移動が増えるため、tabindex設計を見直す。 -
キーボード操作:左右矢印、Home/End をサポート。焦点がカルーセル内にある間のみ反応。
-
自動再生:ユーザーの操作を優先。ホバー/フォーカスで停止、
prefers-reduced-motionではアニメーションを抑制。
7. パフォーマンスと実運用のコツ
-
トランジションは
transformに限定し、left/margin移動は避ける。 -
画像は
loading="lazy"、decoding="async"を併用可能。 -
ビューポート外のスライドに
visibility: hiddenを使うより、overflow: hiddenと移動の方がコストが低い。 -
ウィンドウ幅変更に伴うスナップずれは
ResizeObserverでgoTo(index)を呼び再計算。 -
スライド数が多いときは仮想化(近傍のみ DOM に置く)を検討。
8. よくある不具合と対策
-
ドラッグ後に微妙に位置がずれる:ドラッグ中は
transition: none、確定時にだけ戻す。 -
画像高さでコンテンツジャンプ:
aspect-ratioか固定高さのプレースホルダを設定。 -
オートプレイが止まらない:
mouseenter/focusinなど全停止条件を網羅。visibilitychange(タブ非表示)でも停止。
9. 仕様拡張の例
-
サムネイル列を
aria-controls付きの「タブ」にして直接ジャンプ。 -
スライド内にキャプションを置き、
figcaptionで意味付け。 -
ページネーションを
X / N表示に同期。
ChatGPT5 生成日:2025/09/11