【初心者向け】Javascriptで簡単なTodoアプリの作成【addEventListener】

# 本記事の内容
javascriptで簡単なTodoアプリを作ります。
実装手順は以下の通りです。

①Todoを表示・保存
②Todoを削除
③Todoを完了

# 環境
HTML、CSS部分は今回は範囲外とします。
以下の内容は実装済みの前提で記載していきますので、お使いのテキストエディタへのコピペをお願いします。
![スクリーンショット 2022-09-14 午後5.50.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1973643/25dcc3f2-2e2b-edd7-8192-b3e4e871f140.png)

コードは以下です。
```html:index.html<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
  <title>Document</title>
</head>

<body class="bg-light">
  <div class="container w-75">
    <h1 class="text-center text-info my-4">TODO</h1>
    <form id="form" class="mb-4">
      <input type="text" id="input" class="form-control" placeholder="TODOを入力" autocomplete="off" />
    </form>
    <ul class="list-group text-secondary" id="ul"></ul>
  </div>
  <script src="index.js"></script>
</body>

</html>
```
# ①Todoを表示・保存
* ユーザーの入力値を取得したい
* 取得したデータをHTML上で追加したい
* データを保存したい

## ユーザーの入力値を記録したい
### Document
データを取得したい場合には、Documentを用いる。
DocumentはブラウザAPIであり、ブラウザのデータをプログラムが使いやすいように提供してくれている。

今回はユーザーが入力した値を取得したい。
以下のコードで実行できる。
```:console
document.getElementById("input").value
```
inputはHTMLで指定したidである。
idを入力すると、その情報を取得できる。
inputの中に記載したテキストデータを取得したい場合は、.valueをつけると取得できる。

以下のコードをconsoleに打ち込み、目的のデータを取得できるか試してみる。
### addEventListener
ユーザーが入力し、submitした際のデータを今回は取得したい。
そう言った場合に使用するのがaddEventListener。
特定のイベントが起きたときにjavasripの処理を追加するブラウザAPIの機能である。
```javascript:index.js
const form = document.getElementById("form");
const input = document.getElementById("input");

//ユーザーがsubmitした際に以下の処理を実行する
form.addEventListener("submit", function(event){
//ローディングを防ぐ
event.preventDefault();
//ユーザーが打ち込んだ値を取得
console.log(input.value)
})
```
入力した値がconsole上で表示されたら成功である。

## 取得したフォームの値をHTML上に追加したい
取得した値をHTML上で追加したい。
方法としては、ulとliタグを使って、リストとして追加していく。
```javascript:index.js
const form = document.getElementById("form");
const input = document.getElementById("input");
const ul = document.getElementById("ul");

form.addEventListener("submit", function(event){
event.preventDefault();
add();
})

function add(){
//liを作成
 const li = document.createElement("li");
//ユーザーが入力した値をliとして表示するよう定義
 li.innerText = input.value
//liにクラスを指定
 li.classList.add("list-group-item")
//ulタグの子供としてliを指定
 ul.appendChild(li);
//投稿したら空になるよう設定
 input.value = "";
}
```
![スクリーンショット 2022-09-14 午後8.24.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1973643/1291d855-fced-79bc-7720-0d71e59f507f.png)

### 空タグの生成を防ぐ方法
今のままでは、空の状態でsubmitを押すと、空のliタグが作成されてしまう。
フォームに値が入力されていたら、liタグを作成、そうでなければ作成しない処理を実装する。

使用するのは以下のコードである。
```:
if(条件式){
//条件式の評価結果が
//trueなら実行する処理
}
```
```javascript:index.js
function add(){
    let todoText = input.value
    //todoTextに1文字でも入力されていた場合にtodoTextを返す処理
    let(todoText.length > 0){
     const li = document.createElement("li");
     li.innerText = input.value
     li.classList.add("list-group-item")
     ul.appendChild(li);
     input.value = "";
    }
}
```

## データを保存したい
今のままでは、リロードしたらデータが消えてしまう。
画面に表示されていているデータを取得し、ローカルストレージに保存。ブラウザにデータを保存しておく。

### 画面に表示されている全てのテキストデータの取得
配列のテキスト情報を全て取得したい。その場合使うのがループ。
```:
const array = [1,2,3]
array.forEach(value =>{
    console.log(value * 2)
});
//2,4,6
```

```javascript:index.js
function add(){
    let todoText = input.value
    //todoTextに1文字でも入力されていた場合にtodoTextを返す処理
    let(todoText.length > 0){
     const li = document.createElement("li");
     li.innerText = input.value
     li.classList.add("list-group-item")
     ul.appendChild(li);
     input.value = "";
     saveData();
    }
}

function saveData(){
    //liタグの全ての情報を配列で取得
    const lists = document.querySelectorAll("li")
    //空配列を用意
    let todos =[];
    //liタグの全ての要素に対して処理を行う。
    lists.forEach(list =>{
        todos.push(list.innerText);   
})
}
```

### ローカルストレージへの保存

ローカルストレージへの保存、からの取得は以下のコードで行うことができる。
```
//データの保存
localStorage.setItem('キー','値');

//データの取得
localstorage.getItem('キー');
```
ローカルストレージへの保存
```javascript:index.js
function saveData(){
    //liタグの全ての情報を配列で取得
    const lists = document.querySelectorAll("li")
    //空配列を用意
    let todos =[];
    //liタグの全ての要素に対して処理を行う。
    lists.forEach(list =>{
        todos.push(list.innerText);   
        });
    //データをJSON形式に変換
    localStorage.setItem("todos", JSON.stringify(todos));
}
```
localStorageは文字列形式で保存するため、文字列形式であるJSON形式で保存する必要がある。

### ローカルストレージからデータを取り出す
```javascript:index.js
//JSON.parseにすることで元の配列として扱うことができる。
const todos = JSON.parse(localStorage.getItem("todos"));

//もしtodosが空じゃなかったら処理を実行
if (todos) {
  todos.forEach((todo) => {
    add(todo);
  });
}

function add(todo){
    let todoText = input.value;
    //todoがあった時はtodoTextにtodoのテキストの値を入れる処理 
    if (todo) {
        todoText = todo.text;
    }

    let(todoText){
     const li = document.createElement("li");
     li.innerText = input.value
     li.classList.add("list-group-item")
     ul.appendChild(li);
     input.value = "";
     saveData();
    }
}
```
# ②Todoを削除
右クリックでtodoを削除する機能を追加する。
方法は、addEventListenerで右クリックされたらというイベントを追加する。
```javascript:index.js
function add(todo){
    let todoText = input.value;
    //todoがあった時はtodoTextにtodoのテキストの値を入れる処理 
    if (todo) {
        todoText = todo.text;
    }

    let(todoText){
     const li = document.createElement("li");
     li.innerText = input.value
     li.classList.add("list-group-item")
//ここから追加
    //右クリックしたら処理が実行される。
    li.addEventListener("contextmenu", function(event){
    //右クリックしたら出てくるイベントをブロック
     event.preventDefault();
     li.remove();
     saveData();
    });
//ここまで追加
    ul.appendChild(li);
     input.value = "";
     saveData();
    }
}
```
# ③Todoを完了
クリックしたら打ち消し線をつける処理を実装する。
```javascript:index.js
function add(todo){
    let todoText = input.value;
    //todoがあった時はtodoTextにtodoのテキストの値を入れる処理 
    if (todo) {
        todoText = todo.text;
    }

    let(todoText){
     const li = document.createElement("li");
     li.innerText = input.value
     li.classList.add("list-group-item")

    li.addEventListener("contextmenu", function(event){
     event.preventDefault();
     li.remove();
     saveData();
    });
//ここから追加
    li.addEventListener("click", function(){
      //打ち消し戦をつける
      li.classList.toggle("text-decoration-line-through");
      saveData();
    });
//ここまで追加
    ul.appendChild(li);
     input.value = "";
     saveData();
    }
}
```