JavaScriptのクラスを使ってTODOリスト作ってみた

おはようございます、こんにちは、こんばんわ。

今回はJavaScriptのクラスについて書きます。
クラス使ってみたいけど、機会もないし、そもそもどういう場合に適しているのかいまいち分からない・・・
なので百聞は一コーディングにしかずの精神でTODOリストを書いてみました。

##前置き
新しいもの触るならまずはTODOリスト作ってみるっしょ！って何処かで見聞きした記憶があるのでTODOリストを作ります。
また、クラスに関しては調べれば詳しいことは沢山出てくるので、仕様等については詳しく解説しません。

今回作ったTODOリストはこちら
https://codepen.io/nokonokojr/pen/oNzeWvr


##TODOリストの仕様
- TODOの追加ができること
- TODOの削除ができること
    - チェックボックスでまとめて削除ができること 

（本当はTODOの編集も実装したかったが時間と気力の都合上カット）

##まずは追加機能を作る
とりあえずTODOリストを生成する箱と入力用の`input`と`button`を作ります。

```html:html
<div class="to-do">
  <h1>Class構文でToDoリスト</h1>
  <ul class="to-do-list"></ul>

  <input type="text" class="to-do__text-box" placeholder="TODOを入力">
  <button type="button" class="to-do__add-button">追加</button>
</div>
```
次はJavaScriptでクラスを宣言します。
`constructor`で**TODOリストを入れておく配列**と**使うであろうDOM**を用意しておきます。
追加したTODOを保存しておく機能は作りませんが、それ用に引数も設定。
頭から使う要素などコードに書き出せたのでスッキリ。

```javascript:JavaScript
class toDoList {
  constructor(toDoArray = []) { 
    this.toDoArray = toDoArray
    this.element = {
      toDoList: document.querySelector('.to-do-list')
    }
  }
}
```
初期化処理の部分は一旦書けたので、追加機能用にメソッドを書きます。
`constructor`で用意したTODOリストを入れておく配列に新しくデータを追加します。
引数の`value`には入力フォームの内容が渡される想定。

```javascript:JavaScript
class toDoList {
  constructor(toDoArray = []) {...}

  add(value) {
    this.toDoArray = [...this.toDoArray, value]
  }
}
```
ここまで書いて一旦`html`と機能を繋げて、実際に配列に`value`を入れてみる
宣言したクラスの下に記述してきます。

```javascript:JavaScript
// オブジェクトを作成
const toDoClass = new toDoList()

document.querySelector('.to-do__add-button').addEventListener('click', () =>{
  const value = document.querySelector('.to-do__text-box').value
  toDoClass.add(value)

  // 入力欄を空に
  document.querySelector('.to-do__text-box').value = ''
})
```
特に問題もなく配列に`value`を追加できたので、TODOリストをDOM生成する機能を作ります。
TODOリストの配列を`forEach`で回して一個ずつDOMを生成する感じ・・・
`add`メソッドで`create`メソッドを呼び出すのも忘れずに。

```javascript:JavaScript
class toDoList {
  constructor(toDoArray = []) {...}

  create() {
    // ToDoリストを生成
    this.toDoArray.forEach(toDo => {
      const element = document.createElement('li')
      const text = document.createTextNode(`・${toDo}`)

      element.appendChild(text)
      this.element.toDoList.appendChild(element)
    })
  }

  add(value) {
    this.toDoArray = [...this.toDoArray, value]
    this.create()
  }
}
```
DOMの生成はできたけれども、配列全てが生成されるので前回生成したDOMと合わせてDOTOが雪だるま式に増えていってしまった・・・
前回生成したDOMを全て削除しないと...!!
子要素を削除するのって大変だな・・・(jQueryに感謝)

```javascript:JavaScript
class toDoList {
  constructor(toDoArray = []) {...}

  create() {
    // 表示しているToDoリストを消す
    while (this.element.toDoList.firstChild) {
      this.element.toDoList.removeChild(this.element.toDoList.firstChild);
    }

    // ToDoリストを生成
    this.toDoArray.forEach(toDo => {
      const element = document.createElement('li')
      const text = document.createTextNode(`・${toDo}`)

      element.appendChild(text)
      this.element.toDoList.appendChild(element)
    })
  }

  add(value) {...}
}
```
##削除機能の実装
チェックボックスでまとめて消せるようにするので、チェックボックスを追加しないと・・・
TODOリストを生成する処理にチェックボックスの生成を追加します。

```javascript:JavaScript
class toDoList {
  constructor(toDoArray = []) {...}

  create() {
    // 表示しているToDoリストを消す
    while (this.element.toDoList.firstChild) {
      this.element.toDoList.removeChild(this.element.toDoList.firstChild);
    }

    // ToDoリストを生成
    this.toDoArray.forEach(toDo => {
      const element = document.createElement('li')
      const checkbox = document.createElement('input')
      const text = document.createTextNode(`・${toDo}`)

      checkbox.setAttribute("type","checkbox"); 
      element.appendChild(checkbox)
      element.appendChild(text)
      this.element.toDoList.appendChild(element)
    })
  }

  add(value) {...}
}
```
削除ボタンも`html`に追加します。
タイトル下で良い気がする・・・

```html:html
<div class="to-do">
  <h1>Class構文でToDoリスト</h1>
  <button type="button" class="to-do__remove-button">削除</button>
  <ul class="to-do-list"></ul>

  <input type="text" class="to-do__text-box" placeholder="TODOを入力">
  <button type="button" class="to-do__add-button">追加</button>
</div>
```
モノは揃った気がするので、削除用のメソッドを追加します。
何番目のTODOがチェックされているかを配列でもらって、TODOリスト配列から該当するデータを削除します。
`['TODO1','TODO2','TODO3']`は

- TODO1
- TODO2
- TODO3

とTODOリスト配列の順番通りにDOM生成されているので、辻褄が会うはず！

```javascript:JavaScript
class toDoList {
  constructor(toDoArray = []) {...}

  create() {...}

  add(value) {...}

  remove(removeToDo) {
    descendRemoveToDo.forEach(toDoNumber => {
      this.toDoArray.splice(toDoNumber, 1)
    })
    this.create()
  }
}
```
削除機能を`html`と繋ぎます。
`querySelectorAll`で取得した`NodeList`はそのままTODOリストの順番になるはずなので、`forEach`で`NodeList`を回して`checked`の場合は`index`を配列に追加していきます。
作った配列は`remove`メソッドに渡します。

```javascript:JavaScript
// オブジェクトを作成
const toDoClass = new toDoList()

document.querySelector('.to-do__add-button').addEventListener('click', () =>{...})

document.querySelector('.to-do__remove-button').addEventListener('click', () =>{
  const removeToDo = []
  const checkList = document.querySelectorAll('.to-do-list input[type="checkbox"]')
  checkList.forEach((checkbox, index) => {
    if(checkbox.checked) {
      removeToDo.push(index)
    }
  })
  toDoClass.remove(removeToDo)
})
```
削除はできているけど、２つ以上になるとうまく削除されない・・・
原因はTODOの削除処理は１つずつ削除しているので、削除用の配列とTODOリストの配列の辻褄が合わなくなったためでした。

- 削除用の配列：`[1,2]`

- TODOリストの配列：`['TODO1','TODO2','TODO3']`
で削除処理を通すと2回目の削除を行うときにTODOリストの配列は
`['TODO1','TODO3']`
になってしまうので３番目の配列は存在しないことになり、削除したかった`TODO3`は削除されなくなっていました。
そして、悩んで出した答えは削除用配列を**降順**に並び替えることでした。
後ろの方から削除していけば辻褄が合わなくなることがなくなった！（コードを書いていて気持ちいい瞬間）
降順に並び替えるのは`.sort() 数値並び替え`と調べると出てきます。

```javascript:JavaScript
class toDoList {
  constructor(toDoArray = []) {...}

  create() {...}

  add(value) {...}

  remove(removeToDo) {
    // removeToDoの中身を降順に
    const descendRemoveToDo = removeToDo.sort((a,b) => {
      return b - a
    })

    descendRemoveToDo.forEach(toDoNumber => {
      this.toDoArray.splice(toDoNumber, 1)
    })
    this.create()
  }
}
```
大体実装が終わったので、動作確認をしたら実装漏れがあったので対応。
引数でTODOリストの配列を渡された時の対応ができていなかった・・・
初期化時に走る`constructor`に`create`メソッドを記述します。

```javascript:JavaScript
class toDoList {
  constructor(toDoArray = []) {
    this.toDoArray = toDoArray
    this.element = {
      toDoList: document.querySelector('.to-do-list')
    }

    // 初期化処理時、toDoArrayに中身があるとき
    if(this.toDoArray.length) {
      this.create()
    }
  }

  create() {...}

  add(value) {...}

  remove(removeToDo) {...}
}
```
一旦TODOリストが完成しました。
入力フォーム空っぽのままでもTODO追加できちゃうとか色々やることまだありますが、学習目的ということでご容赦を！

##感想
ビュアなJavaScriptで書くと大変だなと第一に思いました。
クラスについては整理して書かざるを得ない感じなのでいいなと思いました。
WEB制作で使うとなると`main`みたいなクラスを作って`main`をオーバーライドする形で機能を実装していく感じすると良いのかな...??

前置きにも記述しましたが、今回作ったTODOリストはこちら
https://codepen.io/nokonokojr/pen/oNzeWvr