はじめに
とある業務の中で「ライブラリを使わずにスライダーを実装してほしい」という実装指示がありました。
その中で実装したコードについて、他の記事やChatGPTを参照しており自分の中での理解度が弱かった部分があったため、備忘録や自分の中での復習の意味も込めてVanilla JSでのスライダーの実装方法について解説してみようと思います。
実装例
このように画面全体に画像が表示され、一定間隔で左方向に流れ続けるスライダーを想定しています。
(今回の例では画像は4枚を想定しています)
ポイントとしては4枚目から1枚目に遷移する時にも1〜3枚目と同じ挙動をしている点です。このあたりの挙動がVanillaJSでスライダーを作成しようとする際に詰まりがちな点だと思います。
HTML
スライダーを横並びさせる親要素と、その親要素を囲んではみ出しを防止させるための親要素を配置しています。
<div class="slider">
<div class="slider__inner">
<div class="slider__item">
<p>1</p>
</div>
<div class="slider__item">
<p>2</p>
</div>
<div class="slider__item">
<p>3</p>
</div>
<div class="slider__item">
<p>4</p>
</div>
<div class="slider__item">
<p>1</p>
</div>
</div>
</div>
ポイントとしてはスライドの1枚目の画像を(スライド数+1)枚目にも配置するという部分です。
スライドの切り替わりで画像が動くまでの間隔で末尾の画像から1枚目の画像に遷移することで、ユーザー視点で同じ方向に動き続けているように見せることができます。
CSS
.slider{
width: 100%;
overflow: hidden;
}
.slider__inner{
width: 500%; /* 今回はHTMLの画像数が(4+1)=5枚のため */
height: 200px; /* プロジェクトごとに適宜変更してください */
display: flex;
transition: transform 0.5s ease;
}
.slider__item {
width: 100%;
height: 200px; /* プロジェクトごとに適宜変更してください */
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
/* .slider__itemの各要素に背景色をつけるCSS、内側の文字のCSSは省略 */
Vanilla JSのスライダーでは画像の親要素のWidthを(HTML上の画像数 * 100)%に設定します。そうすることでスライドできるだけの領域を確保することができます。
そのままだと画面幅がはみ出てしまうため必ずもう1階層上の要素にwidth: 100%;とoverflow: hidden;を設定してはみ出しを防止するようにするとよいです。
JavaScript
document.addEventListener("DOMContentLoaded", () => {
const sliderInner = document.querySelector(".slider__inner");
const slides = Array.from(sliderInner.children);
let currentIndex = 0;
const switchSlide = () => {
currentIndex = (currentIndex + 1) % slides.length;
if(currentIndex === 4){
sliderInner.style.transform = `translateX(-${currentIndex * (100 / (slides.length))}%)`;
setTimeout(() => {
sliderInner.style.transition = "none";
sliderInner.style.transform = "translateX(0%)";
setTimeout(() => {
sliderInner.style.transition = 'transform 0.5s ease';
switchSlide();
}, 500);
}, 500)
}else{
sliderInner.style.transform = `translateX(-${currentIndex * (100 / (slides.length))}%)`;
}
}
setInterval(() => switchSlide(), 5000);
});
ここから個別のコード解説です。
currentIndex = (currentIndex + 1) % slides.length;
スライダーの順序捕捉変数currentIndexは、スライダーを変化させる処理の際に剰余付きで加算させます。
こうすることでスライダーの処理を繰り返してもindex番号がスライダーの要素数以内に収まるため、エンドレスに処理を続けることができます。
sliderInner.style.transform =
`translateX(-${currentIndex * (100 / (slides.length))}%)`;
基本的なスライダーの処理です。
スライダーの原理として親要素のtransformXを徐々にマイナス方向にずらすことによって画像の動きを実現させています。
(100÷子要素の数)だけ1回スライドするごとに親要素のtransform: transformXの値を変化させており、この動きにより画像を左方向に移動するような動きが実現できます。
const switchSlide = () => {
// ここにスライダーの処理が入ります
}
setInterval(() => switchSlide(), 5000);
スライダー関数はsetIntervalを使うことで一定間隔で実行することができます。これによってスライダーが動く間隔を調整できます。
if(currentIndex === 4){
sliderInner.style.transform = `translateX(-${currentIndex * (100 / (slides.length))}%)`;
setTimeout(() => {
sliderInner.style.transition = "none";
sliderInner.style.transform = "translateX(0%)";
setTimeout(() => {
sliderInner.style.transition = 'transform 0.5s ease';
switchSlide();
}, 500); // transitionで設定した時間と合わせる
}, 500) // transitionで設定した時間と合わせる
}
今回の実装ではHTMLで1枚目と最後の画像を重複させています。そのため、最後の画像になったときは画像を遷移させたあとにトランジションなしで1枚目の画像に戻すという処理を加えます。
今まで通りにスライド処理を行ったあと、transitionの時間分だけ経過したらtransitionの設定を切りつつ親要素のtransformを初期位置に戻す処理を行います。
こうすることでtransitionなしで要素を移動できるので、スライダーの位置を一番最初の位置に戻す動作を閲覧者から気づかれずに行うことができます。
ですが、現状では次回以降のスライドの動作もtransition設定が切れたままです。
画像の位置を変更できたらtransitionの設定を元通りに戻し、再帰的にスライダー関数を実行することで設定を戻してスライダーを動かすことができます。
最後に
ここまで読んでいただきありがとうございます。
今回の記事を通して自分の中でもスライダーの実装のロジックを再認識することができました。
スライダーの実装にはライブラリが便利なのは紛れもない事実ですが、プロジェクトの制約などの関係でライブラリが使えない(or不使用推奨の)ケースは存在すると思います。
そのような場面でこの記事が参考になれば幸いです。
参考文献

Comments
スマホ(iOS + Safari)で見たら、4→1の遷移でスライドせず一瞬で切り替わってました。
こうしたら4→1の遷移時もスムーズにスライドしました。
Let's comment your feelings that are more than good