JavaScriptでTodoアプリを作る

JavaScript勉強したときにTodoを作ったのでその時のメモです。

# 完成イメージ
フォームに値を入力してTodoを作成、作成したTodoは完了リストに移動か削除できる。
完了リストにあるTodoは未完了のTodoリストに戻すことができる。
というものです。
CSSは最低限しか当ててないので今回は割愛。

![ezgif.com-gif-maker (5).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/974719/8fda0880-ca47-708d-43ce-b3949909ba70.gif)


# 実装
## HTML
```html
<!DOCTYPE html>
<html>
  <head>
    <title>TODO(JS)</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div class="input-area">
      <input id="add-text" placeholder="TODOを入力" />
      <button id="add-button">追加</button>
    </div>
    <div class="incomplete-area">
      <p class="title">未完了のTODO</p>
      <ul id="incomplete-list"></ul>
    </div>
    <div class="complete-area">
      <p class="title">完了したTODO</p>
      <ul id="complete-list"></ul>
    </div>

    <script src="src/index.js"></script>
  </body>
</html>
```

## Todo追加
```javascript
import "./styles.css";

const onClickAdd = () => {
  //　テキストボックスの値を取得して初期化
  const inputText = document.getElementById("add-text").value;
  document.getElementById("add-text").value = "";

  // divタグ作成
  const div = document.createElement("div");
  div.className = "list-row";

  // liタグ作成
  const li = document.createElement("li");
  li.innerText = text;

  // 完了ボタン作成
  const completeButton = document.createElement("button");
  completeButton.innerText = "完了";

  // 削除ボタン作成
  const deleteButton = document.createElement("button");
  deleteButton.innerText = "削除";

 //divタグの子要素に各要素を追加
 div.appendChild(li);
 div.appendChild(completeButton);
 div.appendChild(deleteButton);

 // 未完了リストに追加
 document.getElementById("incomplete-list").appendChild(div);

};

document
  .getElementById("add-button")
  .addEventListener("click", () => onClickAdd());
```

### 解説
テキストボックスに入力した値を取得し、TodoのHTML構造が以下のようになるように各要素を作成して追加しています。

```html
<div class="list-row">
  <li>Todo</li>
  <button>完了</button>
  <button>削除</button>
</div>
```

## Todo削除

```javascript
import "./styles.css";

const onClickAdd = () => {
  //　テキストボックスの値を取得して初期化
  const inputText = document.getElementById("add-text").value;
  document.getElementById("add-text").value = "";

  // divタグ作成
  const div = document.createElement("div");
  div.className = "list-row";

  // liタグ作成
  const li = document.createElement("li");
  li.innerText = text;

  // 完了ボタン作成
  const completeButton = document.createElement("button");
  completeButton.innerText = "完了";

  // 削除ボタン作成
  const deleteButton = document.createElement("button");
  deleteButton.innerText = "削除";
//--------------------追加---------------------------------------------------
  deleteButton.addEventListener("click", () => {
    // 削除する部分の親要素を取得
    const deleteTarget = div.parentNode;
    // 親要素から削除
    document.getElementById("incomplete-list").removeChild(deleteTarget);
  });
//--------------------------------------------------------------------------


 //divタグの子要素に各要素を追加
 div.appendChild(li);
 div.appendChild(completeButton);
 div.appendChild(deleteButton);

 // 未完了リストに追加
 document.getElementById("incomplete-list").appendChild(div);

};

document
  .getElementById("add-button")
  .addEventListener("click", () => onClickAdd());
```
### 解説
削除ボタンをクリックしたら`parentNode`で削除する部分を取得し、`removeChild`で削除するfunctionを作りTodoを削除しています。

## Todo完了(共通化)
```javascript
import "./styles.css";

const onClickAdd = () => {
  //　テキストボックスの値を取得して初期化
  const inputText = document.getElementById("add-text").value;
  document.getElementById("add-text").value = "";

  // divタグ作成
  const div = document.createElement("div");
  div.className = "list-row";

  // liタグ作成
  const li = document.createElement("li");
  li.innerText = text;

  // 完了ボタン作成
  const completeButton = document.createElement("button");
  completeButton.innerText = "完了";
//-------------------------追加------------------------------------------
  completeButton.addEventListener("click", () => {
    //押された完了ボタンの親タグをリストから削除
    deleteFromIncompleteList(completeButton.parentNode);
  });
//-----------------------------------------------------------------------

  // 削除ボタン作成
  const deleteButton = document.createElement("button");
  deleteButton.innerText = "削除";
  deleteButton.addEventListener("click", () => {
//-------------------------追加------------------------------------------
    //押された削除ボタンの親タグをリストから削除
    deleteFromIncompleteList(deleteButton.parentNode); 
//-----------------------------------------------------------------------
  });

 //divタグの子要素に各要素を追加
 div.appendChild(li);
 div.appendChild(completeButton);
 div.appendChild(deleteButton);

 // 未完了リストに追加
 document.getElementById("incomplete-list").appendChild(div);

//-------------------------追加------------------------------------------
// targetの親要素を削除
  const deleteFromIncompleteList = (target) => {
    document.getElementById("incomplete-list").removeChild(target);
  };
//-----------------------------------------------------------------------

};

document
  .getElementById("add-button")
  .addEventListener("click", () => onClickAdd());
```
### 解説
削除ボタンと完了ボタンで同じ処理（Todoをリストから消す）は共通化しておきます。

## Todo完了
```javascript
import "./styles.css";

const onClickAdd = () => {
  //　テキストボックスの値を取得して初期化
  const inputText = document.getElementById("add-text").value;
  document.getElementById("add-text").value = "";

  // divタグ作成
  const div = document.createElement("div");
  div.className = "list-row";

  // liタグ作成
  const li = document.createElement("li");
  li.innerText = text;

  // 完了ボタン作成
  const completeButton = document.createElement("button");
  completeButton.innerText = "完了";
  completeButton.addEventListener("click", () => {
    //押された完了ボタンの親タグをリストから削除
    deleteFromIncompleteList(completeButton.parentNode);
//-------------------------追加------------------------------------------
    //完了リストに追加する
    const addTarget = completeButton.parentNode;

    // todo内容の取得
    const text = addTarget.firstElementChild.innerText;

    // 完了リストに追加するために初期化
    addTarget.textContent = null;

    // liタグ作成
    const li = document.createElement("li");
    li.innerText = text;

    // 戻すbutton作成
    const backButton = document.createElement("button");
    backButton.innerText = "戻す";

    // 親要素に子要素追加
    addTarget.appendChild(li);
    addTarget.appendChild(backButton);

    // 完了リストに追加
    document.getElementById("complete-list").appendChild(addTarget);
//---------------------------------------------------------------------

  });

  // 削除ボタン作成
  const deleteButton = document.createElement("button");
  deleteButton.innerText = "削除";
  deleteButton.addEventListener("click", () => {
  //押された削除ボタンの親タグをリストから削除
    deleteFromIncompleteList(deleteButton.parentNode); 
  });

 //divタグの子要素に各要素を追加
 div.appendChild(li);
 div.appendChild(completeButton);
 div.appendChild(deleteButton);

 // 未完了リストに追加
 document.getElementById("incomplete-list").appendChild(div);

// targetの親要素を削除
  const deleteFromIncompleteList = (target) => {
    document.getElementById("incomplete-list").removeChild(target);
  };

};

document
  .getElementById("add-button")
  .addEventListener("click", () => onClickAdd());
```
### 解説
完了リストに以下のHTMLのように追加するので、Todo内容を取得後、初期化して新しく`li`タグと完了ボタンを作成し、完了リストに追加しています。

```html
<div class="list-row">
  <li>TODOです</li>
  <button>戻す</button>
</div>
```

##Todoを戻す

```javascript
import "./styles.css";

const onClickAdd = () => {
  //　テキストボックスの値を取得して初期化
  const inputText = document.getElementById("add-text").value;
  document.getElementById("add-text").value = "";

  createIncompleteList(inputText); //共通化
};

//-------------------------追加------------------------------------------
// targetの親要素を削除
const deleteFromIncompleteList = (target) => {
  document.getElementById("incomplete-list").removeChild(target);
};
//----------------------------------------------------------------------

const createIncompleteList = (text) => {  //共通化
  // divタグ作成
  const div = document.createElement("div");
  div.className = "list-row";

  // liタグ作成
  const li = document.createElement("li");
  li.innerText = text;

  // 完了ボタン作成
  const completeButton = document.createElement("button");
  completeButton.innerText = "完了";
  completeButton.addEventListener("click", () => {
    // リストから削除
    deleteFromIncompleteList(completeButton.parentNode);

    //完了リストに追加する
    const addTarget = completeButton.parentNode;

    // todo内容の取得
    const text = addTarget.firstElementChild.innerText;

    // 完了リストに追加するために初期化
    addTarget.textContent = null;

    // liタグ作成
    const li = document.createElement("li");
    li.innerText = text; // textに変更

    // 戻すbutton作成
    const backButton = document.createElement("button");
    backButton.innerText = "戻す";
//-------------------------追加------------------------------------------
    backButton.addEventListener("click", () => {
      // 完了リストから削除
      const deleteTaget = backButton.parentNode;
      document.getElementById("complete-list").removeChild(deleteTaget);

      // テキスト取得
      const text = backButton.parentNode.firstElementChild.innerText;
      createIncompleteList(text);
    });
//----------------------------------------------------------------------

    // 親要素に子要素追加
    addTarget.appendChild(li);
    addTarget.appendChild(backButton);

    // 完了リストに追加
    document.getElementById("complete-list").appendChild(addTarget);
  });
  // 削除ボタン作成
  const deleteButton = document.createElement("button");
  deleteButton.innerText = "削除";
  deleteButton.addEventListener("click", () => {
    deleteFromIncompleteList(deleteButton.parentNode);
  });

  // divタグの下にliタグを配置
  div.appendChild(li);
  div.appendChild(completeButton);
  div.appendChild(deleteButton);

  // 未完了リストを配置
  document.getElementById("incomplete-list").appendChild(div);
};

document
  .getElementById("add-button")
  .addEventListener("click", () => onClickAdd());
```
### 解説
フォームに値を入力し追加ボタンを押して未完了リストに使いした処理と同じなので共通化しています。

これで完成！！

# 参考

https://www.udemy.com/course/modern_javascipt_react_beginner/