スライドショーを作りつつ文法のおさらいをする

##はじめに
JavaScriptの基本文法はひと通りやったけども...この後どうするの？と悩む少し前の私へ。今までに習ったこの文法で、スライドショーができるよ！
この記事は、自動で切り替わるスライドショーをつくりながら使用文法を簡単におさらいしていく記事です。

おさらいする文法は、次の通り。

- 要素の取得とカウント
- 関数（即時関数）
- 配列と要素の生成
- for文
- 関数（関数宣言）
- if文
- classの追加と削除
- addEventListener
- 三項演算子
- タイマー処理
- タイマー処理の解除

##これからつくるモノ
<p class="codepen" data-height="500" data-theme-id="light" data-default-tab="js,result" data-user="non_123" data-slug-hash="ExjNGpZ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="slider_fadeinout">
  <span>See the Pen <a href="https://codepen.io/non_123/pen/ExjNGpZ">
  slider_fadeinout</a> by Nozomi (<a href="https://codepen.io/non_123">@non_123</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

###仕様
⑴子要素数に合わせてインジケーターをJSで生成する
⑵スライド要素切り替わりの条件は、①指定した秒数が経過する、または、②インジケーターをクリックする
⑶スライド要素にホバーしている間は、自動切り替えが止まる

##HTML/CSSのつくり
```html:index.html
<ul id="js-slider_wrap">
  <li class="show">1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>
<ul id="js-slider_dot"></ul>
```
\#js-slider_wrapの子要素、liをposition: absolute;で同じ位置に重ね、更にopacity: 0;で見えなくしています。class="show"が付いたli要素のみopacity: 1;となり、見えるようになる、という仕組みです。
ここからJSを使って『class="show"を順番に追加/削除することで表示される子要素が切り替える動き』をつくります。
##JavaScriptを組み立てる
段階ごとに切り出して説明していきますが、[先ほどのCodePen](https://codepen.io/non_123/pen/ExjNGpZ)を別タブで開いて全体像も確認しながら見ていただく方が、わかりやすいかもです。

###①必要な要素を取得する
```index.js
(function(d) {
  //①要素の取得
  const slide = d.getElementById('js-slider_wrap');
  const slideitem = slide.getElementsByTagName('li');
  const num = slide.childElementCount;
})(document);
```
まずは操作するDOM要素と子要素数を取得します。
定数『num』には、slideの子要素数をカウントして格納しています。
####おさらい ： 要素の取得とカウント
```
//IDで要素を取得
document.getElementById("ID名");

//要素の種類から要素を取得
document.getElementsByTagName("要素名")[n番目の要素];

//子要素の数をカウント
document.childElementCount;
```
ID取得の際は、同じIDは存在しないため『Element』と単数形です。
一方Tag要素は複数存在できるので、『Elements』と複数形になります。[ ]内で、取得した要素のうちの何番目に処理を適用するのかを数字で書きます。カウントは0から始まるので、１番目の要素を指すときは[0]と書きます。
####おさらい：関数（即時関数）
```
(function(仮引数1,仮引数2) {
　//処理
}(引数1,引数2);
```
これは使用しなくてもスライドショーはできますが、勉強なので使ってみました。
即時関数は、スクリプトが読み込まれたらすぐに実行される関数です。{}に処理を書くことでローカルスコープができるので、他箇所のスクリプトとバッティングしない便利な書き方です。
引数にdocumentを渡して仮引数に省略形を設定すれば、この後のコードもスッキリ書けます。
###②インジケーターをつくる
仕様⑴子要素数に合わせてインジケーターをJSで生成する、の部分をつくります。

```index.js
  //②インジケーターを作る
  const dotwrap = d.getElementById('js-slider_dot'); 
  const dot = dotwrap.getElementsByTagName('li');

  let li = [];
  for (let i = 0; i < num; i++) {
    li.push(d.createElement('li')); //li要素を生成して配列liに入れる
    dotwrap.appendChild(li[i]); //dotwrapの子要素に配列liの中身を追加する
  }
  dotwrap.firstElementChild.classList.add('active');
```
現在位置を示すインジケーターを、先ほど取得した子要素数numと同じだけJSで作ります。
dotwrapでインジケーターを作成する位置を取得しています。dotは次のfor文で生成したliを取得しています。この過程では使いませんが、インジケーターに関する要素なので、まとめてこの部分に記載しておきます。
####おさらい ： 配列と要素の生成
```
//空の配列を作成
var sampleArray = [];

//要素を生成
document.createElement('要素');

//要素を配列の最後に追加する
sampleArray.push(document.createElement('要素'));
```
配列は、複数の値を１つのデータにまとめて扱うことのできるデータ構造です。
配列自体のデータ型はオブジェクトで、中には数値、文字列、オブジェクトとどのデータ型でも格納できます。
####おさらい ： for文
```
for (初期化式; 条件式; 加算式) {
　//処理
}
```
条件式の値がtrueの間、処理→加算式の順に繰り返されます。
加算式でよく表記される「i++」の書き方は「インクリメント演算子」と呼ばれ、変数iの初期値に1追加した後の数値を変数iに再代入しています（= 変数iが+1ずつ増えていく）。
反対に1ずつ減らす場合は「デクリメント演算子」と呼ばれ、「i--」という書き方をします。

---
ここまでできたら、以下のように動きのないスライドができているかと思います。
<img width="1115" alt="ここまでの完成図" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459055/527cbef9-9fa3-e0c6-2737-80111bc5f7cc.png">

次からは、スライドの肝となる動きの部分、仕様⑵スライド要素切り替わりの条件は、①指定した秒数が経過する、または、②インジケーターをクリックする、をつくっていきます。

###③index数を制御する関数を作る
スライドショー部分は『class="show"を順番に追加/削除することで表示される子要素が切り替える』仕組みです。この『何番目の要素にclass="show"を追加/削除するのか』の『何番目』の部分を③で作っていきます。

```index.js
  //③index数を制御するfunctionを作る
  let cur_index = 0;  //番号の初期設定
  function count_index() {
    cur_index++;　//functionが動くと番号が1ずつ追加される
    if (cur_index === num) {　//番号が子要素数と同数になったら、
      cur_index = 0; //番号をリセット （= 0番目の要素に戻す）
    }
  }
```
『何番目』部分を変数cur_indexとして、初期値を0に設定しています。
関数count_indexが動くごとにcur_indexは１ずつ増え、もし子要素数と同数（= 最後の要素）になったら、cur_indexをリセットして0番目の要素に戻るようにします。
####おさらい：関数（関数宣言）
````
//関数宣言
function 関数名(引数1, 引数2,...) {
　//処理
}

//関数の呼び出し
関数名(引数A, 引数B,...);
````
任意の関数名をつけて宣言する書き方です。先に出てきた即時関数と異なり、処理を動かすには関数の呼び出しが必要になります。
関数の呼び出し時に引数を渡した場合、その引数を関数の処理内で変数のように使うことができます。引数の数に制限はなく、また、無しでも大丈夫です。

####おさらい：if文
````
if (条件式１) {
 　//条件式１がtrueの時の処理
} else if (条件式２) {
　　//条件式２がtrueの時の処理
} else {
　　//条件式１、２以外の時の処理
}
````
条件によって処理を分岐することができます。
条件は上から順に判別されていくので条件式の順番にも注意が必要です。

###④インジケーターのクリックで表示要素を切り替える関数をつくる

````index.js
  //④インジケーターのクリックで表示要素を切り替え
  function click_dot() {
    for (let j = 0; j < num; j++) {
      dot[j].addEventListener('click', function() {
        dot[cur_index].classList.remove('active'); //cur_index番目のdot（li要素）から.activeを削除
        dot[j].classList.add('active'); //j番目のdot（li要素）に.activeを追加

        slideitem[cur_index].classList.remove('show');　//cur_index番目のslideitemから.activeを削除
        slideitem[j].classList.add('show');　　//cur_index番目のslideitemに.activeを追加

        cur_index = j;　//cur_indexをjと合わせる
      });
    }
  }
````
インジケーターがクリックされたら、インジケーターとスライド要素のクラスを操作して表示要素を切り替えています。cur_indexはこの後作成する⑤スライドの切り替え用関数でも現在位置を示すために利用しているので、インジケーターのクリックによってスライド要素が切り変わった場合には『cur_index = j』番目に上書きします。

####おさらい：classの追加と削除
```
//classを追加
document.getElementsByClassName[0].classList.add('クラス名');

//classを削除
document.getElementsByClassName[0].classList.remove('クラス名');
```
####おさらい：addEventListener 
```index.js
イベント対象の要素.addEventListener('イベントタイプ', function () {
　//処理
}, false);
```
addEventListenerは、「ページが読み込まれた時」「要素がクリックされた時」といったイベント処理を実行できるメゾットです。
イベント対象の要素には、イベントを発火させる対象の要素を書きます。
イベントタイプには、click、scrollといったイベントの種類を「" "」または「' '」で囲って書きます。指定したイベントタイプが、対象要素に対して起こると、処理が実行されます。
falseの部分ではイベントの伝播方式を指定できます。デフォルト値はfalseで、falseの場合は表記を省略することができます。

###⑤スライド切り替え用関数をつくる
```index.js
  function fade_infinite() {
    count_index(); //③で作成した関数でcount_indexの制御を呼び出す
    let rm_index = (cur_index == 0 ? num - 1 : cur_index - 1); //何番目の要素のクラスを削除するのかを設定

    slideitem[rm_index].classList.remove('show'); 
    slideitem[cur_index].classList.add('show');

    dot[rm_index].classList.remove('active');
    dot[cur_index].classList.add('active');

    click_dot(cur_index); //④で作成した関数でインジケータークリック時の処理を呼び出す
  }
```
何番目のスライド要素のクラスを削除するのかを、変数rm_indexで制御しています。
現在位置（cur_index）が０番目の時は最後の要素番号、０以外の時は現在位置の１つ前の要素番号が変数に入れられています。

####おさらい：三項演算子
```
条件式 ? trueの場合の処理　: falseの場合の処理
```
条件式の結果で処理が切り替わる、if文の書き換えバージョンです。
あまり複雑な分岐だとコードが分かりにくくなりますが、今回のようにシンプルな条件分岐をしたい時には簡潔なコードにすることができます。

###⑥スライド切り替えを自動的に行う関数をつくる
```index.js
  //自動切り替えをスタートする関数
  const sec = 3000; //表示する時間
  let timer;
  function start_timer() {
    timer = setInterval(fade_infinite, sec);
  }
  start_timer();　//関数の呼び出し
```
タイマー処理のメゾットを使って、先ほど⑤で作成したスライド切り替え用関数を一定時間ごとに発動させる関数をつくっています。


####おさらい：タイマー処理
````
//決められた時間間隔で繰り返し処理を実行する
setInterval(関数, 時間);

//決められた時間が経過したら１回だけ処理を実行する
setTimeout(関数,時間);
````
タイマー処理には２種類あり、今回は繰り返し実行したいのでsetIntervalを利用します。時間部分はミリ秒で表します。どちらもタイマーIDを返すようになっていて、

```
var timer = setInterval(関数, 時間);
```
のように、タイマーIDを変数に格納して使うことが多いです。

----

ここまでできたら、仕様の
⑴子要素数に合わせてインジケーターをJSで生成する
⑵スライド要素切り替わりの条件は、①指定した秒数が経過する、または、②インジケーターをクリックする
ができましたね...！最後に、
⑶スライド要素にホバーしている間は、自動切り替えが止まる
をつくりましょう。

###⑦ホバーしている間は自動切り替えをストップする
```index.js
　 //タイマー処理をストップする関数
  function stop_timer() {
    clearInterval(timer);
  }
  
  for (let k = 0; k < num; k++) {
    slideitem[k].addEventListener('mouseover', () => { 
      stop_timer(); //スライド要素にホバーしたら、タイマー処理をストップ
    });
    slideitem[k].addEventListener('mouseleave', () => {
      start_timer();　//スライド要素からホバーが外れたら、タイマー処理をスタート
    });
  }
```
####おさらい：タイマー処理の解除
````
//clearInterval()でセットしたタイマーを解除する
clearInterval(対象のタイマーID);

//setTimeout()でセットしたタイマーを解除する
clearTimeout(対象のタイマー処理ID);
````
setIntervalやsetTimeoutで返されたID（ここではtimer）を渡すことで、タイマー処理をストップすることができます。

----
お疲れさまでした！
これで、自動で切り替わるスライドの完成です。

##おわりに
文章もコードも、スッキリ書くのは難しいですね...!

間違いや「ここはこの書き方がいいよ！」といったアドバイスがありましたら、お知らせいただけると嬉しいです。ここまで読んでいただきありがとうございました！