まずは見た目を整える
今回はJavaScriptのコードを中心に解説していこうと思うのですが、まずは見た目の調整(CSS)についてざっくり説明していきます。
いきなりですが、HTMLとCSSの完成コードを見ていきましょう。ポイントとなる部分は後ほど解説を加えます。
box-sizingは全ての要素でborder-boxであるものとします。
<div class="slider">
<!-- 画像表示部分 -->
<div class="slider_outer">
<div class="slider_container">
<div class="slider_item">
<img src="donut01.webp" alt="チョコレートドーナツ" width="250" height="250">
</div>
<div class="slider_item">
<img src="donut02.webp" alt="ホワイトチョコレートドーナツ" width="250" height="250">
</div>
<div class="slider_item">
<img src="donut03.webp" alt="ハニーシロップドーナツ" width="250" height="250">
</div>
</div>
</div>
<!-- 進むボタンと戻るボタン -->
<button type="button" class="next_btn">▶️</button>
<button type="button" class="back_btn">◀️</button>
<!-- ドットインジケータ -->
<div class="indicator_container">
<div class="indicator indicator1"></div>
<div class="indicator"></div>
<div class="indicator"></div>
</div>
</div>
/* 画像スライダー */
.slider {
width: 300px;
height: 300px;
display: grid;
justify-content: center;
position: relative;
}
.slider_outer {
position: relative;
width: 250px;
height: 250px;
overflow: hidden;
}
.slider_container {
display: flex;
column-gap: 30px;
transition: .5s ease-in-out;
}
/* 進む・戻るボタン */
button {
all: initial;
width: 30px;
height: 30px;
border-radius: 50%;
font-size: 24px;
cursor: pointer;
}
button:active {
opacity: .6;
}
.next_btn {
position: absolute;
color: lightcoral;
top: 110px;
right: -20px;
}
.back_btn {
position: absolute;
color: lightgray;
top: 110px;
left: -15px
}
/* ドットインジケータ */
.indicator_container {
display: flex;
justify-content: center;
column-gap: 30px;
}
.indicator {
width: 10px;
height: 10px;
background-color: lightgray;
border-radius: 50%;
}
.indicator1 {
background-color: gray;
}
表示結果(画像)
それでは、コードのポイントとなる部分に解説を加えていきます。
HTMLの構造は見ての通りです。全体をdiv要素(class=”slider”)でマークアップし、ドットインジケータ等を含むスライダーの幅と高さ(width/height)を指定しています。
さらに、position: relativeとすることで基準位置になるように指定し、このdiv要素を基準として、進む・戻るボタンの位置をposition: absoluteで調整しています。
一番重要なポイントは、画像が横並びになるように指定(display: flex)した上で、親要素からはみ出る部分をクリップ(overflow: hidden)しているという点です。
ここでは、3枚の画像を格納しているslider_containerクラスのdiv要素でdisplay: flexを指定し、その親要素であるslider_outerクラスのdiv要素にoverflow: hiddenを指定していることに注目してください。
もし、overflowの指定がなければ、表示結果は下の画像のようになります。
表示結果(画像)
overflowをhiddenに指定することで見せたくない部分をクリップして隠しておき、slider_containerクラスのdiv要素をtranslateXで横軸方向に動かすことで画像スライダーになります。
また、初期状態(チョコレートドーナツの画像が表示されている状態)の時は、左の戻るボタンをクリックしても意味がないことをわかりやすくするため、戻るボタンの色をlightgray、一番左側のドットインジケータの色をgrayにしています。
戻る・進むボタンの色とドットインジケータの色は、状態によって色が切り替わるように、この後JavaScriptで調整を加える必要があります。
次のステップでは、JavaScriptコードを記述する前にまず状況を整理して、どのような仕組みにすればコードが簡単に書けるようになるかを考えてみましょう。
POINT!
- ・全体をマークアップする要素でスライダーの高さと幅を設定しよう!
- ・displayをflexにして画像を横並びにし、親要素からはみ出る部分はoverflowをhiddenにしてクリップしよう!
- ・slider_containerクラスのdiv要素を横に動かすことでスライダーになる!
場合分けで状況を整理する
複雑な機能を実装したい場合、いきなりコードを書き始めてもうまくいかなかったり、無駄が多くて冗長なコードになってしまいがちです。
なので、コードを書き始める前にまずしっかりと状況を整理して、『どんな時にどんな機能が求められるか?』を正確に把握することが大切だと言えます。
ということで、さっそく状況を整理してみましょう。今回のサンプルスライダーの状況は、次のようになっています。
- ・画像は全部で3枚で横並びになっており、初期位置は一番左
- ・進む/戻るボタンと、ドットインジケータがある
画像が3枚ということは、管理しなければならない状態も3つということになりますね。
具体的には次の3つの状態を適切に管理することが求められます。
- (1)一番左の画像が表示されている時
- (2)真ん中の画像が表示されている時
- (3)一番右の画像が表示されている時
そして、それぞれの状態の時に求められる機能は次のようになります。
スマホでは表を横にスクロールできます。
パーツ\状態 |
(1)の時 |
(2)の時 |
(3)の時 |
進むボタン |
機能する |
機能する |
機能しない |
戻るボタン |
機能しない |
機能する |
機能する |
ドットインジケータ |
一番左 |
真ん中 |
一番右 |
どの状態の時に何をすれば良いか、大分わかりやすくなりましたね。
状態管理の方法は色々考えられますが、今回はそれぞれの状況に応じて変数に1〜3の整数を代入することで、状態を管理してみたいと思います。
たとえば、let sliderStatus = 1(初期状態)と定義するとしたら、(2)の時は2を、(3)の時は3をsliderStatus変数に代入すれば、現在どの状態にあるかを常に把握することができるようになります。
そして、このように論理的にロジックを作り上げていくことこそが、プログラミングにおいて最も重要な過程です。
プログラミングスキルを磨く=メソッドを一つでも多く覚えるとか、小手先のテクニックに意識が向いてしまいがちですが、メソッドを覚えることにあまり意味はありません。
どんなメソッドも使う機会がなければいずれ忘れてしまいますし、必要に応じてその都度調べて使いこなすことができれば、覚えておく必要はありません。
それよりも、アルゴリズムやオブジェクト指向など、プログラミングの根本的な考え方や仕組みを理解し、適切にロジックを組み立てられるようになっておくことの方が大切だと言えるでしょう。
話が横道に逸れてしまいましたが、次のステップでJavaScriptコードを書き、スライダーを完成させていきましょう!
POINT!
- ・複雑な機能を実装したい場合は、まず状況をしっかりと整理することから始めよう!
- ・今回の例では、3つの状態を管理する必要がある!
- ・状況に応じて変数に1〜3までの整数を格納することで、画像の表示状態管理が容易になる!
switch文で状態に応じた処理を行う
管理しなければならない状態が、たとえば真と偽の2つだけであればif文でシンプルに記述できますが、今回のように複数の状態を管理しなければならないケースではswitch文が便利です。
複数の条件に応じた処理はif文でもできるのですが、else if〜が重なると条件分岐がわかりづらくなるというデメリットがあります。
一方、switch文であれば条件分岐をよりシンプルに記述することができるため、条件に応じた処理の流れが分かりやすくなります。
switch文のシンプルな構文は次の通りです。
- switch文の構文
switch (条件となる変数や式など) {
case value1: ※条件と比較する値
処理
break;
case value2:
処理
break;
// …
case valueN:
処理
break;
default:
処理
}
defaultはどのcaseにも当てはまらない場合に行われる処理ですが、これは省略可能です。
今回は1〜3の整数(状態)のうち、必ずいずれかの数値に該当するように設計しているため、default節は省略します。
また、Swiftではbreakがなくてもswitch文から抜け出せますが、JavaScriptではbreakをつけ忘れると、次のbreakまで処理が続けて行われてしまいます。
なので、『ここで処理を止めたい』という場合はbreakを忘れないように気をつけましょう。(今回のスライダー実装でも必要です)
以上を踏まえると、今回のswitch文は次のようになります。
// スライダーの状態(初期は1)
let sliderStatus = 1;
switch(sliderStatus) {
case 1:
// 状態が(1)の時の処理
break;
case 2:
// 状態が(2)の時の処理
break;
case 3:
// 状態が(3)の時の処理
break;
}
あとは、進むボタンもしくは戻るボタンがクリックされた時に、必要な処理を行うようにすればOKですね。
クリックイベントは使用頻度がかなり高く、説明を必要としない人も多いかと思うので、ここではaddEventListener(click)の詳しい説明は省略します。
switch文で適切に場合分けができていれば、その後の処理は難しくはありません。それでは、完成系のJavaScriptコードを確認していきましょう!
// スライダーコンテナ、進む・戻るボタン、ドットインジケータの要素を取得
const sliderContainer = document.querySelector(".slider_container");
const nextBtn = document.querySelector(".next_btn");
const backBtn = document.querySelector(".back_btn");
const indicators = document.querySelectorAll(".indicator");
// スライダーの状態(初期は1)
let sliderStatus = 1;
// 進むボタンが押された時
nextBtn.addEventListener("click", () => {
switch(sliderStatus) {
case 1: // (1)の時の処理
sliderStatus = 2;
sliderContainer.style.transform = `translateX(-${280 * 1}px)`;
backBtn.style.color = "lightcoral";
indicators[0].style.backgroundColor = "lightgray";
indicators[1].style.backgroundColor = "gray";
break;
case 2: // (2)の時の処理
sliderStatus = 3;
sliderContainer.style.transform = `translateX(-${280 * 2}px)`;
nextBtn.style.color = "lightgray";
indicators[1].style.backgroundColor = "lightgray";
indicators[2].style.backgroundColor = "gray";
break;
case 3: // (3)の時の処理
break;
}
});
// 戻るボタンが押された時
backBtn.addEventListener("click", () => {
switch(sliderStatus) {
case 1: // (1)の時の処理
break;
case 2: // (2)の時の処理
sliderStatus = 1
sliderContainer.style.transform = `translateX(${0}px)`;
backBtn.style.color = "lightgray";
indicators[1].style.backgroundColor = "lightgray";
indicators[0].style.backgroundColor = "gray";
break;
case 3: // (3)の時の処理
sliderStatus = 2;
sliderContainer.style.transform = `translateX(-${280 * 1}px)`;
nextBtn.style.color = "lightcoral";
indicators[2].style.backgroundColor = "lightgray";
indicators[1].style.backgroundColor = "gray";
break;
}
});
ということで、スライダーが完成しました!
ドットインジケータは、それぞれ一意となるidやclassを指定して、querySelector()で一つ一つ要素を取得しても良いのですが、今回はquerySelectorAll()でまとめて取得し、取得した順番の配列で管理しています。
一番左のドットインジケータから順に、配列番号は0、1、2…となります。
また今回、画像をスライドさせる距離は280pxを基準にしていますが、適切な距離はスライダーの幅、画像の大きさ、column-gapの大きさなどにより変わります。
移動距離に関しては、どんな画像でも同じように調整すれば良いというわけではないので注意が必要です。
なお、(3)の状態の時の進むボタンと、(1)の状態の時の戻るボタンは、押されても何も処理を行う必要がない状況なのでbreakで処理から抜けるようにしています。
さて、今回はライブラリに頼らずスライダーを実装してみましたが、皆さんはどのように感じたでしょうか?
想像よりも、けっこう簡単に素のJSでスライダーが作れたと感じたのではないかと思います。
筆者がWeb制作に関わり始めてまだ間もない頃は、ライブラリ無しでスライダーを実装するなんて絶対無理だと思っていました。それほど、ハードルが高く感じられたんですね。
でもプログラミングの基本をしっかり学んで、データや処理の流れを整理できるようになってからは、一見複雑そうに見えるものでも細かく分解していけばなんとか作れそうだと感じることが多くなりました。
実務では、効率やメンテナンスの容易さなどを考慮するとライブラリを頼った方が良い場面が多いのですが、趣味や学習としてプログラミングを楽しむ時は、ライブラリを頼らず自分で作ってみるのも良い経験になりますよ!
POINT!
- ・複数の条件別に処理を分けたいときは、switch文が便利で分かりやすい!
- ・switch文では、breakで処理を抜けるのを忘れないようにしよう!
- ・default節はどのcaseにも当てはまらない場合に行われる処理で、省略可能!