JavaScript02 自作スライドショーで動きの基礎を段階的に学ぶ

JavaScriptを使った動きのあるWebサイトは、より魅力的で印象に残るものです。ここでは、初心者の方を対象に、Web制作で役立つ『スライドショー』をゼロから自作してみます。
最初はシンプルな構造から始め、フェード効果、操作ボタン、自動再生、インジケーターの追加、そしてレスポンシブ対応まで段階的に進めます。これにより、JavaScriptを使った動きの基礎を自然に習得できます。
プログラミング初心者の方でも一歩一歩進められる内容となっていますので、一緒にスライドショー制作の楽しさを体感してみましょう!
基本的なスライドショー
まずは、HTMLとCSSだけで、画像を自動で切り替えるスライドショーを作成します。CSSもアニメーション用のプロパティを導入するだけで、JavaScriptを使わなくても十分にスライドさせることが可能です。ここでは、固定幅の800px*400pxの画像幅のスライダーです。
See the Pen CSSだけで作成するスライダー by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
HTMLの解説
<div class="slideshow"> <div class="slides"> <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1"> <img src="https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02" alt="Slide 2"> <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3"> </div>
<div class="slideshow">
はスライドショー全体を囲むコンテナ。<div class="slides">
はスライドを横並びに表示するための要素。<img>
はスライドショーの画像要素。今回はダミー画像生成サイト(placehold.jp)より作成しています。
CSSの解説
.slideshow { width: 800px; height: 400px; overflow: hidden; position: relative; } .slides { display: flex; width: 100%; height: 100%; animation: slide 9s infinite; } .slides img { width: 800px; height: 400px; flex-shrink: 0; object-fit: cover; } @keyframes slide { 0% { transform: translateX(0); } 33.33% { transform: translateX(-800px); } 66.66% { transform: translateX(-1600px); } 100% { transform: translateX(0); } }
.slideshow
- スライドショー全体を囲む部分です。
- 横幅800px、高さ400pxの枠を作り、画像がその中に収まるようにしています。
overflow: hidden;
を使って、枠からはみ出した画像部分は見えなくしています。
.slides
- 複数の画像を横一列に並べています。
display: flex;
を使います。 - 後でアニメーションで画像が左右に動くようにするため、幅や高さをスライドショーと同じに設定
- 複数の画像を横一列に並べています。
.slides img
- スライドショーの画像そのものです。
- 画像サイズをスライドショー枠のサイズ(800px × 400px)に合わせるため、
width
とheight
を設定しています。 - 画像が縦横比を崩さず収まるように、
object-fit: cover;
を指定。
@keyframes slide
- スライドショーの画像を自動で切り替えるための「動き」を定義する部分です。
@keyframes
はCSSでアニメーションを作るときに使います。動きの始まりから終わりまでを時間の割合で設定します。
- 0%: 最初の画像を表示。
- 33.33%: 次の画像が表示されるように、スライド全体を左に800px移動(
transform: translateX(-800px);
)。 - 66.66%: さらに次の画像が表示されるように、左に1600px移動。
- 100%: 最初の位置(0px)に戻る。
- アニメーションの設定
.slides
に対して、animation: slide 9s infinite;
を設定しています。slide
は上で定義した動きを指します。9s
はアニメーションが1回終わるまでにかかる時間(9秒)。infinite
はアニメーションを無限に繰り返すという指定。
JavaScriptを使ったスライドショー
HTML/CSSスライダーは、CSSアニメーション(@keyframes
)とtransform: translateX
を使って、画像を一定間隔で自動的に切り替えます。画像は横並び(flex
)で配置し、CSSの@keyframes
でアニメーションをループさせて切り替えを実現しました。コードはとても軽量で簡単です。
ただ、柔軟性という点では低く、切り替え間隔やスピードを動的に変えることができなかったり、ボタンやインジケーターなどの操作を追加するのも難しくなってきます。以下からは、JavaScriptを使ったスライドショーを作成していきます。
See the Pen setIntervalを使ったスライドショー by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
HTMLの解説
<div class="slideshow"></div>
- スライドショー全体を囲むコンテナ。
- この中に、JavaScriptで生成した画像を動的に追加します。
- 初期状態では空です(画像はJavaScriptで追加されます)←大事
CSSの解説
.slideshow { width: 800px; height: 400px; overflow: hidden; position: relative; } .slideshow img { width: 800px; height: 400px; object-fit: cover; position: absolute; top: 0; left: 0; opacity: 0; transition: opacity 1s ease-in-out; /* フェードアニメーション */ } .slideshow img.active { opacity: 1; /* 表示する画像の透明度を変更 */ }
.slideshow
- コンテナの幅と高さを指定。
overflow: hidden;
で、コンテナ外にはみ出した要素を非表示にします。position: relative;
にすることで、内部の画像要素を相対位置で基準を決めます。
.slideshow img
- すべての画像をコンテナの中に重ねて配置(
position: absolute;
)。 - 初期状態では画像の透明度(
opacity
)を0
に設定し、見えない状態にします。 transition
でフェードアニメーションを設定し、opacity
が変更されると滑らかに変化します。
- すべての画像をコンテナの中に重ねて配置(
.slideshow img.active
- 表示する画像に
active
クラスを付与。 opacity: 1
で画像を表示。
- 表示する画像に
JavaScriptの解説
画像データの設定
const images = [ "https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01", "https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02", "https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" ];
- 配列imagesに画像のURLを格納。
- 動的に画像を生成するため、この配列をループして画像を追加します。
スライドショーの画像を追加
const slideshow = document.querySelector('.slideshow'); images.forEach((src, index) => { const img = document.createElement('img'); // 新しい<img>要素を生成 img.src = src; // 画像のURLを設定 img.alt = `Slide ${index + 1}`; // 代替テキストを設定 if (index === 0) img.classList.add('active'); // 最初の画像に`active`クラスを付与 slideshow.appendChild(img); // スライドショーコンテナに追加 });
- document.querySelector(‘.slideshow’)
- HTML内にある、classの.slideshow要素を取得して、画像を追加する対象とします。
- images.forEach
- 配列内のすべての画像URLをループして処理します。
forEach
は、配列内のすべての要素を順番に取り出し、それぞれに対して指定した処理を実行するためのメソッドです。- src部分に画像が入り、index部分にキーが入ります、 最初のループは、画像ファイル,0となり、次のループは次の画像,1となります。
- img.classList.add(‘active’)
- 最初の画像(インデックス0)のみactiveクラスを付与して表示。
具体的な処理の流れ(トレース)
- 1回目のループ:
src
:"https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01"
index
:0
- 新しい
<img>
要素を作成。 src
属性に画像URLを設定。alt
属性にSlide 1
を設定。index === 0
の条件を満たすため、この画像にactive
クラスを付与。- コンテナ(
<div class="slideshow">
)に追加。
- 2回目のループ:
src
:"https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02"
index
:1
- 新しい
<img>
要素を作成。 src
属性に画像URLを設定。alt
属性にSlide 2
を設定。index === 0
の条件を満たさないため、active
クラスは付与しない。- コンテナに追加。
- 3回目のループ:
src
:"https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03"
index
:2
- 同様の手順で、新しい
<img>
要素を作成し、設定。 - コンテナに追加。
タイマー設定
setInterval(() => changeImage(), 3000);
- 3秒(3000ミリ秒)ごとにchangeImage関数を実行。
- 自動で画像が切り替わるスライドショーを実現。
JavaScript:配列を使わないスライドショー
02では、JavaScript内の配列で画像を指定した場合でしたが、画像部分はHTMLで準備したい・・・という要望がありましたので以下は、画像をHTMLで配置し、JavaScript では、img要素を取得して処理をする内容となります。CSS部分は02の内容と同じです。
See the Pen Untitled by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
HTMLの解説
<div class="slideshow"> <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1" class="active"> <img src="https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02" alt="Slide 2"> <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3"> </div> <script src="script.js"></script>
画像を静的に記述します。img
要素を3つ直接HTML内に配置し、それをJavaScriptで切り替えます。
JavaScript構成
// HTML内のすべての画像を取得 const slides = document.querySelectorAll('.slideshow img'); // 現在のスライドインデックスを初期化 let currentIndex = 0; // 画像を切り替える関数 const changeImage = () => { slides[currentIndex].classList.remove('active'); // 現在の画像を非表示 currentIndex = (currentIndex + 1) % slides.length; // 次の画像のインデックスを計算 slides[currentIndex].classList.add('active'); // 次の画像を表示 }; // 3秒ごとに画像を切り替える setInterval(() => changeImage(), 3000);
document.querySelectorAll('.slideshow img')
で、すべての画像要素を取得。changeImage
関数内で、現在の画像からactive
クラスを外し、次の画像にactive
クラスを付与。setInterval
で3秒ごとにchangeImage
を実行し、画像を切り替え。
JavaScript:「次へ」「前へ」ボタンを追加
HTMLに直接画像を記述する方法のまま、「次へ」「前へ」ボタンを追加していきます。まずは手動で、次へ、前へをクリックして画像が変わるようにします。
See the Pen 前へ次へ スライドショーjs by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
HTML
<div class="slideshow"> <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1" class="active"> <img src="https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02" alt="Slide 2"> <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3"> <button class="prev">前へ</button> <button class="next">次へ</button> </div> <script src="script.js"></script>
CSS
.slideshow { width: 800px; height: 400px; overflow: hidden; position: relative; } .slideshow img { width: 800px; height: 400px; object-fit: cover; position: absolute; top: 0; left: 0; opacity: 0; transition: opacity 1s ease-in-out; /* フェードアニメーション */ } .slideshow img.active { opacity: 1; /* 表示する画像の透明度を変更 */ } button { position: absolute; top: 50%; transform: translateY(-50%); background-color: rgba(0, 0, 0, 0.5); color: #fff; border: none; padding: 10px 20px; cursor: pointer; font-size: 16px; } button.prev { left: 10px; /* 左端に配置 */ } button.next { right: 10px; /* 右端に配置 */ }
JavaScriptの解説
// スライドショーの要素を取得 const slides = document.querySelectorAll('.slideshow img'); const prevButton = document.querySelector('.prev'); const nextButton = document.querySelector('.next'); // 現在のスライドインデックス let currentIndex = 0; // 画像を切り替える関数 const showSlide = (index) => { slides.forEach((slide, i) => { slide.classList.toggle('active', i === index); // 現在のスライドにactiveクラスを付与 }); }; // 「次へ」ボタンのクリックイベント nextButton.addEventListener('click', () => { currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動 showSlide(currentIndex); }); // 「前へ」ボタンのクリックイベント prevButton.addEventListener('click', () => { currentIndex = (currentIndex - 1 + slides.length) % slides.length; // 前のスライドに移動 showSlide(currentIndex); });
document.querySelectorAll('.slideshow img')
: HTML内のすべての画像を取得。showSlide(index)
:指定されたインデックスの画像にactive
クラスを付与し、他の画像からactive
クラスを削除。
イベントリスナー
addEventListener
は、特定のイベント(クリック、マウスオーバーなど)を監視し、そのイベントが発生したときに実行する処理(コールバック関数)を指定するメソッドです。
// 「次へ」ボタンのクリックイベント nextButton.addEventListener('click', () => { currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動 showSlide(currentIndex); });
nextButton.addEventListener('click', ...)
:- ボタン(
nextButton
)にクリックイベント('click'
)を登録。 - ボタンがクリックされると、登録された関数(以下のアロー関数)が実行されます。
- ボタン(
currentIndex = (currentIndex + 1) % slides.length
:currentIndex
は現在表示中のスライドを指すインデックスです。currentIndex + 1
: 次のスライドに進むためにインデックスを1つ増加させます。% slides.length
: スライドの総数(slides.length
)で割った余りを計算。- これにより、最後のスライド(インデックスが最大値)から次に進むと、最初のスライド(インデックス0)に戻る動きが実現します。
showSlide(currentIndex)
:- 現在のスライドを表示するための関数を呼び出し、スライドを切り替えます。
// 「前へ」ボタンのクリックイベント prevButton.addEventListener('click', () => { currentIndex = (currentIndex - 1 + slides.length) % slides.length; // 前のスライドに移動 showSlide(currentIndex); });
prevButton.addEventListener('click', ...)
:- ボタン(
nextButton
)にクリックイベント('click'
)を登録。 - ボタンがクリックされると、登録された関数(以下のアロー関数)が実行されます。
- ボタン(
currentIndex = (currentIndex - 1 + slides.length) % slides.length
:currentIndex - 1
: 前のスライドに移動するためにインデックスを1つ減少。+ slides.length
:- インデックスが負の値になる場合を防ぐために、スライド総数(
slides.length
)を加算。 - JavaScriptでは負のインデックスをサポートしていないため、こうすることで負の値を正しいインデックスに変換。
- インデックスが負の値になる場合を防ぐために、スライド総数(
% slides.length
:- インデックスがスライド総数の範囲(0から
slides.length - 1
)内に収まるように調整。
- インデックスがスライド総数の範囲(0から
showSlide(currentIndex)
:- 現在のスライドを表示するための関数を呼び出し、スライドを切り替えます。
現在のスライドを表示するshowSlide
関数
const showSlide = (index) => { slides.forEach((slide, i) => { slide.classList.toggle('active', i === index); // 現在のスライドにactiveクラスを付与 }); };
slides.forEach((slide, i) => {...})
:- スライドショー内のすべての画像要素をループ処理。
- 現在のスライドにのみ
active
クラスを付与。
slide.classList.toggle('active', i === index)
i === index
: 現在のスライドかどうかを判定。- toggle関数を使い、
true
の場合、active
クラスを付与。 - toggle関数を使い、
false
の場合、active
クラスを削除。
前へ次へのボタン プラス 最初からスライドしたい
ボタンを追加するJavaScriptでは、クリックイベントの学習でしたので、最初からスライドしません。ここでは、自動スライドページを表示すると、スライドショーが自動で動き始め、手動操作の優先「次へ」「前へ」ボタンをクリックすると、操作中は自動スライドが一時停止し、再び自動スライドが再開されるように追加してみましょう。HTML/CSSはそのまま使います。
See the Pen 前へ次へプラス自動スライド by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
JavaScriptの解説
自動スライドの機能を追加します。
// スライドショーの要素を取得 const slides = document.querySelectorAll('.slideshow img'); const prevButton = document.querySelector('.prev'); const nextButton = document.querySelector('.next'); // 現在のスライドインデックス let currentIndex = 0; // 画像を切り替える関数 const showSlide = (index) => { slides.forEach((slide, i) => { slide.classList.toggle('active', i === index); // 現在のスライドにactiveクラスを付与 }); }; // 自動スライドの間隔 const autoSlideInterval = 3000; // 3秒 // 自動スライドをスタート let autoSlide = setInterval(() => { currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動 showSlide(currentIndex); }, autoSlideInterval); // 「次へ」ボタンのクリックイベント nextButton.addEventListener('click', () => { clearInterval(autoSlide); // 自動スライドを一時停止 currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動 showSlide(currentIndex); autoSlide = setInterval(() => { // 再度自動スライドを開始 currentIndex = (currentIndex + 1) % slides.length; showSlide(currentIndex); }, autoSlideInterval); }); // 「前へ」ボタンのクリックイベント prevButton.addEventListener('click', () => { clearInterval(autoSlide); // 自動スライドを一時停止 currentIndex = (currentIndex - 1 + slides.length) % slides.length; // 前のスライドに移動 showSlide(currentIndex); autoSlide = setInterval(() => { // 再度自動スライドを開始 currentIndex = (currentIndex + 1) % slides.length; showSlide(currentIndex); }, autoSlideInterval); });
- 自動スライドの追加
setInterval
で3秒ごとにcurrentIndex
を増加し、スライドを切り替えています。showSlide(currentIndex)
で切り替えを実行。
- クリック操作の一時停止と再開
- 「次へ」「前へ」ボタンをクリックすると、現在の
setInterval
をclearInterval
で一時停止。 - ボタン操作後に新たな
setInterval
を再設定し、自動スライドを再開します。
- 「次へ」「前へ」ボタンをクリックすると、現在の
- スライドのループ処理
currentIndex = (currentIndex + 1) % slides.length
: 最後のスライドから次へ移動すると最初に戻る。currentIndex = (currentIndex - 1 + slides.length) % slides.length
: 最初のスライドから前へ移動すると最後に戻る。
JavaScript:インジケーターを設置
次は、前へ・次へのボタン、自動再生に加え、インジケーターを追加したスライドショーになります。インジケーターは、画像の下部にある丸のボタンで、今何枚目の写真が表示されているかを明示したり、クリックすることで写真が移動したりする目安のボタンです。
See the Pen 前へ次へ・インジケーターjs by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
HTMLの解説
<div class="slideshow"> <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1" class="active"> <img src="https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02" alt="Slide 2"> <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3"> <button class="prev">前へ</button> <button class="next">次へ</button> <div class="indicators"></div> </div> <script src="script.js"></script>
<div class=”indicators”></div> が追加されます。
CSSの解説
.slideshow { width: 800px; height: 400px; overflow: hidden; position: relative; } .slideshow img { width: 800px; height: 400px; object-fit: cover; position: absolute; top: 0; left: 0; opacity: 0; transition: opacity 1s ease-in-out; /* フェードアニメーション */ } .slideshow img.active { opacity: 1; /* 表示する画像の透明度を変更 */ } button { position: absolute; top: 50%; transform: translateY(-50%); background-color: rgba(0, 0, 0, 0.5); color: #fff; border: none; padding: 10px 20px; cursor: pointer; font-size: 16px; } button.prev { left: 10px; /* 左端に配置 */ } button.next { right: 10px; /* 右端に配置 */ } .indicators { position: absolute; bottom: 15px; left: 50%; transform: translateX(-50%); display: flex; gap: 10px; } .indicators .indicator { width: 15px; height: 15px; background-color: rgba(255, 255, 255, 0.5); border-radius: 50%; cursor: pointer; } .indicators .indicator.active { background-color: rgba(255, 255, 255, 1); }
.indicators
- スライドショーの下に配置されるインジケーター(丸い要素)の親要素。
- 中央揃えで横一列に並べられています。
- 各インジケーターの間隔は10pxです。
.indicator
- 各スライドを表す丸い要素。
- サイズは15pxで、半透明の白い背景。
- 丸い形状(
border-radius: 50%
)で、クリック可能に設定しています。
.indicator.active
現在表示中のスライドに対応するインジケーターを完全な白色で強調表示します。
JavaScript構成
// スライドショーの要素を取得 const slides = document.querySelectorAll('.slideshow img'); const prevButton = document.querySelector('.prev'); const nextButton = document.querySelector('.next'); const indicatorsContainer = document.querySelector('.indicators'); // 現在のスライドインデックス let currentIndex = 0; // 画像を切り替える関数 const showSlide = (index) => { slides.forEach((slide, i) => { slide.classList.toggle('active', i === index); // 現在のスライドにactiveクラスを付与 }); updateIndicators(index); // インジケーターを更新 }; // インジケーターを作成 slides.forEach((_, index) => { const indicator = document.createElement('div'); indicator.classList.add('indicator'); if (index === 0) indicator.classList.add('active'); // 最初のインジケーターにactiveクラス indicator.addEventListener('click', () => { clearInterval(autoSlide); // 自動スライドを一時停止 currentIndex = index; // インジケーターのクリックでスライドを切り替え showSlide(currentIndex); restartAutoSlide(); // 自動スライドを再開 }); indicatorsContainer.appendChild(indicator); }); // インジケーターを更新 const updateIndicators = (index) => { const indicators = document.querySelectorAll('.indicator'); indicators.forEach((indicator, i) => { indicator.classList.toggle('active', i === index); // 現在のスライドに対応するインジケーターを強調 }); }; // 自動スライドの間隔 const autoSlideInterval = 3000; // 3秒 // 自動スライドをスタート let autoSlide = setInterval(() => { currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動 showSlide(currentIndex); }, autoSlideInterval); // 自動スライドを再開する関数 const restartAutoSlide = () => { autoSlide = setInterval(() => { currentIndex = (currentIndex + 1) % slides.length; showSlide(currentIndex); }, autoSlideInterval); }; // 「次へ」ボタンのクリックイベント nextButton.addEventListener('click', () => { clearInterval(autoSlide); // 自動スライドを一時停止 currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動 showSlide(currentIndex); restartAutoSlide(); // 自動スライドを再開 }); // 「前へ」ボタンのクリックイベント prevButton.addEventListener('click', () => { clearInterval(autoSlide); // 自動スライドを一時停止 currentIndex = (currentIndex - 1 + slides.length) % slides.length; // 前のスライドに移動 showSlide(currentIndex); restartAutoSlide(); // 自動スライドを再開 });
インジケーターを動的に生成
slides.forEach((_, index) => { const indicator = document.createElement('div'); indicator.classList.add('indicator'); if (index === 0) indicator.classList.add('active'); // 最初のインジケーターにactiveクラス indicator.addEventListener('click', () => { clearInterval(autoSlide); // 自動スライドを一時停止 currentIndex = index; // インジケーターのクリックでスライドを切り替え showSlide(currentIndex); restartAutoSlide(); // 自動スライドを再開 }); indicatorsContainer.appendChild(indicator); });
slides.forEach
:- スライドショーの画像数(
slides
の数)に基づいてインジケーターを生成します。 - 画像が3枚ならインジケーターも3つ。
- スライドショーの画像数(
document.createElement('div')
:<div>
要素を動的に生成して、インジケーターとして使用。
indicator.classList.add('indicator')
:- 新しいインジケーターに
indicator
クラスを付与。 - CSSでスタイルを適用するためのクラス設定。
- 新しいインジケーターに
if (index === 0)
:- 最初のインジケーターに
active
クラスを付与し、初期表示を強調。
- 最初のインジケーターに
indicator.addEventListener('click', () => {...})
:- インジケーターがクリックされたときの処理を定義。
- クリックされたインジケーターに対応するスライドを表示するため、次の処理を実行します。
clearInterval(autoSlide)
:- 自動スライドを一時停止。
currentIndex = index
:- クリックされたインジケーターに対応するスライドのインデックスを設定。
showSlide(currentIndex)
:- 該当スライドを表示。
restartAutoSlide()
:- 再度、自動スライドを再開。
indicatorsContainer.appendChild(indicator)
:- 生成したインジケーターを
.indicators
コンテナに追加。
- 生成したインジケーターを
インジケーターの更新
const updateIndicators = (index) => { const indicators = document.querySelectorAll('.indicator'); indicators.forEach((indicator, i) => { indicator.classList.toggle('active', i === index); // 現在のスライドに対応するインジケーターを強調 }); };
querySelectorAll('.indicator')
:- すべてのインジケーターを取得。
indicators.forEach((indicator, i) => {...})
:- 各インジケーターをループして処理。
indicator.classList.toggle('active', i === index)
:- 現在のスライド(
index
)に対応するインジケーターにactive
クラスを付与。 - 他のインジケーターからは
active
クラスを削除。
- 現在のスライド(
自動スライドとの連携
既存の自動スライド機能(setInterval
)やボタンのイベントリスナーと組み合わせて、インジケーターが常に最新の状態になるように更新されています。
let autoSlide = setInterval(() => { currentIndex = (currentIndex + 1) % slides.length; // 次のスライドに移動 showSlide(currentIndex); // スライドを切り替え }, autoSlideInterval);
showSlide(currentIndex)
内でupdateIndicators
が呼び出されるため、自動スライド中もインジケーターが同期して更新されます。
右から左へのスライドイン効果へ変更
ここからは、スライドの動きをフェードから「スライドイン」へ変更します。その際、「無限ループ風スライドショー」になるようにすることでスライドの動きがスムーズになります。
See the Pen 前へ次へインジケーター スライドイン by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
HTMLの解説
<div class="slideshow"> <div class="slides"> <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3"> <!-- ダミー最後 --> <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1"> <img src="https://placehold.jp/ffcb5c/ffffff/800x400.jpg?text=slide02" alt="Slide 2"> <img src="https://placehold.jp/4991ee/ffffff/800x400.jpg?text=slide03" alt="Slide 3"> <img src="https://placehold.jp/410042/ffffff/800x400.jpg?text=slide01" alt="Slide 1"> <!-- ダミー最初 --> </div> <button class="prev">前へ</button> <button class="next">次へ</button> <div class="indicators"></div> </div>
スライドの最初と最後にダミー画像を追加。実際には「01 → 02 → 03 → ダミー01」となり、スライド全体がループしているように見えます。
CSSの解説
.slides { display: flex; transition: transform 1s ease-in-out; transform: translateX(-800px); /* 最初のスライドに移動 */ } .slideshow { width: 800px; height: 400px; overflow: hidden; position: relative; } .slideshow img { width: 800px; height: 400px; object-fit: cover; } button { position: absolute; top: 50%; transform: translateY(-50%); background-color: rgba(0, 0, 0, 0.5); color: #fff; border: none; padding: 10px 20px; cursor: pointer; font-size: 16px; } button.prev { left: 10px; } button.next { right: 10px; } .indicators { position: absolute; bottom: 15px; left: 50%; transform: translateX(-50%); display: flex; gap: 10px; } .indicators .indicator { width: 15px; height: 15px; background-color: rgba(255, 255, 255, 0.5); border-radius: 50%; cursor: pointer; } .indicators .indicator.active { background-color: rgba(255, 255, 255, 1); }
.slides
にtransform: translateX()
を使用して、画像全体を左右に移動させる。transition: transform 1s ease-in-out;
でアニメーションをスムーズに。.slides
の初期位置をダミー最初の次(実スライド最初)に設定。
JavaScriptの解説
const slides = document.querySelector('.slides'); const slideImages = document.querySelectorAll('.slides img'); const prevButton = document.querySelector('.prev'); const nextButton = document.querySelector('.next'); const indicatorsContainer = document.querySelector('.indicators'); let currentIndex = 1; // 最初のスライド(ダミーを考慮して2番目) const totalSlides = slideImages.length; const autoSlideInterval = 3000; // 3秒 // スライドを移動する関数 const moveSlide = (index) => { slides.style.transition = 'transform 1s ease-in-out'; // スムーズに動く slides.style.transform = `translateX(${-index * 800}px)`; }; // インジケーターを更新する関数 const updateIndicators = (index) => { const indicators = document.querySelectorAll('.indicator'); indicators.forEach((indicator, i) => { indicator.classList.toggle('active', i === index - 1); // ダミーを考慮して調整 }); }; // 位置リセット関数(無限ループ風に見せる) const resetPosition = () => { slides.style.transition = 'none'; // トランジションを無効化 if (currentIndex === totalSlides - 1) { currentIndex = 1; // ダミー最初から実スライド最初に移動 slides.style.transform = `translateX(${-currentIndex * 800}px)`; } else if (currentIndex === 0) { currentIndex = totalSlides - 2; // ダミー最後から実スライド最後に移動 slides.style.transform = `translateX(${-currentIndex * 800}px)`; } }; // 自動スライド let autoSlide = setInterval(() => { currentIndex++; moveSlide(currentIndex); setTimeout(resetPosition, 1000); // トランジション終了後に位置リセット }, autoSlideInterval); // 手動操作でスライドを再開 const restartAutoSlide = () => { clearInterval(autoSlide); autoSlide = setInterval(() => { currentIndex++; moveSlide(currentIndex); setTimeout(resetPosition, 1000); }, autoSlideInterval); }; // 「次へ」ボタン nextButton.addEventListener('click', () => { clearInterval(autoSlide); currentIndex++; moveSlide(currentIndex); setTimeout(resetPosition, 1000); restartAutoSlide(); }); // 「前へ」ボタン prevButton.addEventListener('click', () => { clearInterval(autoSlide); currentIndex--; moveSlide(currentIndex); setTimeout(resetPosition, 1000); restartAutoSlide(); }); // インジケーター作成 for (let i = 0; i < totalSlides - 2; i++) { const indicator = document.createElement('div'); indicator.classList.add('indicator'); if (i === 0) indicator.classList.add('active'); indicator.addEventListener('click', () => { clearInterval(autoSlide); currentIndex = i + 1; // ダミーを考慮 moveSlide(currentIndex); setTimeout(resetPosition, 1000); restartAutoSlide(); }); indicatorsContainer.appendChild(indicator); }
ダミースライドを考慮した移動(moveSlide
関数)
const moveSlide = (index) => { slides.style.transition = 'transform 1s ease-in-out'; // アニメーションを適用 slides.style.transform = `translateX(${-index * 800}px)`; // 現在のスライド位置に応じて移動 };
slides.style.transition
:- スライド移動時のアニメーション(トランジション)を設定。
transform 1s ease-in-out
で1秒間スムーズに移動。
slides.style.transform
:- **
translateX
**を使って、スライド全体を左右に移動。 -index * 800
は現在のスライド位置を基に計算(1スライド = 800px幅)。
- **
ループをスムーズにするためのリセット(resetPosition
関数)
const resetPosition = () => { slides.style.transition = 'none'; // トランジションを無効化 if (currentIndex === totalSlides - 1) { currentIndex = 1; // ダミー最初から実スライド最初に移動 slides.style.transform = `translateX(${-currentIndex * 800}px)`; } else if (currentIndex === 0) { currentIndex = totalSlides - 2; // ダミー最後から実スライド最後に移動 slides.style.transform = `translateX(${-currentIndex * 800}px)`; } };
slides.style.transition = 'none'
:- リセット時にトランジションを無効化。
- トランジションが有効なままだと、リセット時にスライドがアニメーションし、ループが不自然に見えます。
- 条件分岐 (
if
,else if
):currentIndex === totalSlides - 1
:- 最後のスライド(ダミー最初)に到達した場合、最初の実スライドに移動。
currentIndex = 1
で位置を調整。
currentIndex === 0
:- 最初のスライド(ダミー最後)に到達した場合、最後の実スライドに移動。
currentIndex = totalSlides - 2
で位置を調整。
slides.style.transform
:- トランジションなしでスライド位置を瞬時に更新し、ループしているように見せます。
自動スライドとの連携
let autoSlide = setInterval(() => { currentIndex++; moveSlide(currentIndex); setTimeout(resetPosition, 1000); // トランジション終了後に位置リセット }, autoSlideInterval);
currentIndex++
:- スライドを右に1つ進める。
moveSlide(currentIndex)
:- 現在のスライド位置に応じてアニメーションを実行。
setTimeout(resetPosition, 1000)
:- トランジションの終了(1秒後)に
resetPosition
を呼び出し、位置をリセット。
- トランジションの終了(1秒後)に
手動操作(ボタンやインジケータークリック)との連携
nextButton.addEventListener('click', () => { clearInterval(autoSlide); // 自動スライドを一時停止 currentIndex++; moveSlide(currentIndex); setTimeout(resetPosition, 1000); // トランジション終了後に位置リセット restartAutoSlide(); }); prevButton.addEventListener('click', () => { clearInterval(autoSlide); // 自動スライドを一時停止 currentIndex--; moveSlide(currentIndex); setTimeout(resetPosition, 1000); // トランジション終了後に位置リセット restartAutoSlide(); });
setTimeout(resetPosition, 1000)
:- トランジション終了後に位置をリセットし、不自然な動きを防ぎます。
restartAutoSlide()
:- ボタン操作後に自動スライドを再開します。
インジケータークリック時の調整
indicator.addEventListener('click', () => { clearInterval(autoSlide); // 自動スライドを一時停止 currentIndex = i + 1; // ダミーを考慮してインデックスを調整 moveSlide(currentIndex); setTimeout(resetPosition, 1000); // トランジション終了後に位置リセット restartAutoSlide(); });
currentIndex = i + 1
:- ダミースライドの影響を考慮して、インジケーターのインデックスを調整。
- 実際のスライドに対応する正しい位置に移動。
レスポンシブにも対応したい!
今までのコードはピクセルを使った固定幅でしたので、CSSとJavaScriptを少し調整し、スライドショーをレスポンシブ対応に変えておきましょう。HTMLはそのまま使います。
See the Pen レスポンシブ対応 by Yoshiko Nakamura (@Yoshiko-Nakamura) on CodePen.
CSSの調整
スライドショーの幅を固定値から割合(%
)やビューポートの幅(vw
)を使用した柔軟な指定に変更します。
.slideshow { width: 100%; /* スライドショー全体を画面幅に合わせる */ max-width: 800px; /* 最大幅を指定 */ height: auto; overflow: hidden; position: relative; } .slides { display: flex; transition: transform 1s ease-in-out; } .slideshow img { width: 100%; /* 画像の幅をスライドショーの幅に合わせる */ height: auto; /* 縦横比を保持 */ } button { position: absolute; top: 50%; transform: translateY(-50%); background-color: rgba(0, 0, 0, 0.5); color: #fff; border: none; padding: 10px 20px; cursor: pointer; font-size: 16px; } button.prev { left: 10px; } button.next { right: 10px; } .indicators { position: absolute; bottom: 15px; left: 50%; transform: translateX(-50%); display: flex; gap: 10px; } .indicators .indicator { width: 15px; height: 15px; background-color: rgba(255, 255, 255, 0.5); border-radius: 50%; cursor: pointer; } .indicators .indicator.active { background-color: rgba(255, 255, 255, 1); }
JavaScriptの調整
スライドの幅を画面サイズに合わせて再計算するように変更します。スライド幅(slideWidth
)を動的に取得し、ウィンドウリサイズ時にもスライド幅を再計算しておきましょう。
const slides = document.querySelector('.slides'); const slideImages = document.querySelectorAll('.slides img'); const prevButton = document.querySelector('.prev'); const nextButton = document.querySelector('.next'); const indicatorsContainer = document.querySelector('.indicators'); let currentIndex = 1; // ダミースライドを考慮 const autoSlideInterval = 3000; // 3秒 let slideWidth = slides.clientWidth; // スライド幅を動的に取得 // スライドの幅を再計算する関数 const updateSlideWidth = () => { slideWidth = slides.clientWidth; // 現在のスライドショーの幅を取得 }; // スライドを移動する関数 const moveSlide = (index) => { slides.style.transition = 'transform 1s ease-in-out'; slides.style.transform = `translateX(${-index * slideWidth}px)`; }; // 位置リセット関数 const resetPosition = () => { slides.style.transition = 'none'; if (currentIndex === slideImages.length - 1) { currentIndex = 1; slides.style.transform = `translateX(${-currentIndex * slideWidth}px)`; } else if (currentIndex === 0) { currentIndex = slideImages.length - 2; slides.style.transform = `translateX(${-currentIndex * slideWidth}px)`; } }; // 自動スライド let autoSlide = setInterval(() => { currentIndex++; moveSlide(currentIndex); setTimeout(resetPosition, 1000); }, autoSlideInterval); // ウィンドウリサイズ時の対応 window.addEventListener('resize', () => { updateSlideWidth(); moveSlide(currentIndex); // リサイズ後の位置を再調整 }); // 手動操作のイベントリスナー nextButton.addEventListener('click', () => { clearInterval(autoSlide); currentIndex++; moveSlide(currentIndex); setTimeout(resetPosition, 1000); }); prevButton.addEventListener('click', () => { clearInterval(autoSlide); currentIndex--; moveSlide(currentIndex); setTimeout(resetPosition, 1000); }); // インジケーター作成とクリックイベント for (let i = 0; i < slideImages.length - 2; i++) { const indicator = document.createElement('div'); indicator.classList.add('indicator'); if (i === 0) indicator.classList.add('active'); indicator.addEventListener('click', () => { clearInterval(autoSlide); currentIndex = i + 1; moveSlide(currentIndex); setTimeout(resetPosition, 1000); }); indicatorsContainer.appendChild(indicator); }
slides.clientWidth
の解説
スライドショーがレスポンシブ対応している場合、画面サイズに応じてスライドの幅(slideWidth
)も動的に変わります。slideWidth
を元にtranslateX
でスライド全体の移動距離を計算します。
let slideWidth = slides.clientWidth;
clientWidth
- HTML要素のコンテンツ部分の幅を取得するプロパティ。
- パディングを含みますが、ボーダーやマージンは含みません。
- 例:
slides
が親要素で、幅をwidth: 100%;
で指定している場合、現在の画面幅に応じたスライドショーの幅を取得。 - 画面幅 800px →
clientWidth
= 800 - 画面幅 400px →
clientWidth
= 400
resize
イベントの解説
ウィンドウのサイズが変更されたときに発生するイベントを作成しておきます。ユーザーがブラウザのウィンドウサイズを変更した際に動作します。
window.addEventListener('resize', () => { updateSlideWidth(); moveSlide(currentIndex); // リサイズ後の位置を再調整 });
updateSlideWidth
関数
updateSlideWidth
関数で、slides.clientWidth
を再取得し、slideWidth
を更新。画面サイズが変わるとスライドの幅も変わるため、それに対応するための計算を行います。
const updateSlideWidth = () => { slideWidth = slides.clientWidth; // 現在のスライドショーの幅を取得 };
moveSlide(currentIndex)
を呼び出し、現在表示中のスライドの位置を正確に更新。リサイズ直後でも、スライドが正しい位置に保持されます。