[JavaScript][L4] コンテンツ(div>ul>li)の生成機能を共通化(コンポーネント化)する

# 実施条件
[[JavaScript][L3] ボタン押下によりコンテンツ(div>ul>li)を別コンテンツ(div>ul>li)に移動(生成)する](https://qiita.com/Jochun/items/7e359c00ef096fa80cec)を理解していること

# 環境
MacBook Pro (2.3 GHz 8コアIntel Core i9)
macOS 14.0（23A344）
Homebrew 4.3.8
gh 2.52.0

# 共通化(コンポーネント化)する理由
- `onClick`イベントを使って実装するとネストが深くなるため
- 共通した機能は、共通化した方が保守性が上がるため

# 共通化(コンポーネント化)に至る状況
`未完了のToDo`(`div`)内に新たなコンテンツを生成する機能が以下の通り複数存在する
- テキストボックスからの`value`取得=`追加`ボタンの押下
- [[JavaScript][L5] ボタン押下によりコンテンツ(div>ul>li)を別コンテンツ(div>ul>li)に移動(生成)する ([L4]で作成したコンポーネントを活用)](https://qiita.com/Jochun/items/7ab0664e4ce022e0a948)で実装する`戻る`ボタンの押下



# ソースコード外観
## HTML
~~~html:index.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">
        <!--  // 完成系の例としてliタグ以下を作成している
        <li>
          <div class="list-row">
            <p class="todo-item">TODOです</p>
            <button>完了</button>
            <button>削除</button>
          </div>
        </li> -->
      </ul>
    </div>
    <div class="complete-area">
      <p class="title">完了したTODO</p>
      <ul id="complete-list">
        <!--  // 完成系の例としてliタグ以下を作成している
        <li>
          <div class="list-row">
            <p class="todo-item">TODOでした</p>
            <button>戻す</button>
          </div>
        </li> -->
      </ul>
    </div>
    <script src="src/index.js"></script>
  </body>
</html>
~~~

## JavaScript
~~~js:index.js
import "./styles.css";

const onClickAdd = () => {
  // テキストボックスの値を取得し、初期化する
  const inputText = document.getElementById("add-text").value;
  document.getElementById("add-text").value = "";
  
  // 未完了リストに追加
  createIncompleteTodo(inputText);
  };

// 渡された引数を基に未完了のTODOを作成する関数
const createIncompleteTodo = (todo) => {
  // li生成
  const li = document.createElement("li");

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

  // p生成
  const p = document.createElement("p");
  p.className = "todo-item";
  p.innerText = todo;

  // button(完了)タグ生成
  const completeButton = document.createElement("button");
  completeButton.innerText = "完了";
  completeButton.addEventListener("click", () => {
    // 押された完了ボタンの親にあるliタグ配下の完了ボタンと削除ボタンを削除
    const moveTarget = completeButton.closest("li");
    completeButton.nextElementSibling.remove();
    completeButton.remove();
    // 戻すボタンを生成してdivタグ配下に設定
    const backButton = document.createElement("button");
    backButton.innerText = "戻す";
    moveTarget.firstElementChild.appendChild(backButton);
    // 完了リストに移動
    document.getElementById("complete-list").appendChild(moveTarget);
  });

  // button(削除)タグ生成
  const deleteButton = document.createElement("button");
  deleteButton.innerText = "削除";
  deleteButton.addEventListener("click", () => {
    // 押された削除ボタンの親にあるliタグを未完了リストから削除
    const deleteTarget = deleteButton.closest("li");
    document.getElementById("incomplete-list").removeChild(deleteTarget);
  });

  // liタグの子要素に各要素を設定
  div.appendChild(p);
  div.appendChild(completeButton);
  div.appendChild(deleteButton);
  li.appendChild(div);

  // id="incomplete-list"の`ul`タグに子要素を設定した`li`タグを追加
  document.getElementById("incomplete-list").appendChild(li);    
  };

document.getElementById("add-button").addEventListener("click", onClickAdd);
~~~

# 手順
1. 共通化する関数のアロー関数を用意する
    ~~~js:index.js
    // 渡された引数を基に未完了のTODOを作成する関数
    const createIncompleteTodo = (todo) => {  
    };
    ~~~
1. 既に存在しているコンテンツ(div>ul>li)を用意したアロー関数に貼り付ける
    ~~~diff_JavaScript:index.js
    // 渡された引数を基に未完了のTODOを作成する関数
    const createIncompleteTodo = (todo) => {
    +   // li生成
    +   const li = document.createElement("li");
    + 
    +   // div生成
    +   const div = document.createElement("div");
    +   div.className = "list-row";
    + 
    +   // p生成
    +   const p = document.createElement("p");
    +   p.className = "todo-item";
    +   p.innerText = inputText;
    + 
    +   // button(完了)タグ生成
    +   const completeButton = document.createElement("button");
    +   completeButton.innerText = "完了";
    +   completeButton.addEventListener("click", () => {
    +     alert("完了");
    +   });
    + 
    +   // button(削除)タグ生成
    +   const deleteButton = document.createElement("button");
    +   deleteButton.innerText = "削除";
    +   deleteButton.addEventListener("click", () => {
    +     alert("削除");
    +   });
    + 
    +   // liタグの子要素に各要素を設定
    +   div.appendChild(p);
    +   div.appendChild(completeButton);
    +   div.appendChild(deleteButton);
    +   li.appendChild(div);
    + 
    +   // 未完了リストに追加
    +   document.getElementById("incomplete-list").appendChild(li);
    };
    ~~~

1. 引数が揃っていないため修正する
    ~~~diff_JavaScript:index.js
    // 渡された引数を基に未完了のTODOを作成する関数
    const createIncompleteTodo = (todo) => {
        // li生成
        const li = document.createElement("li");
      
        // div生成
        const div = document.createElement("div");
        div.className = "list-row";
      
        // p生成
        const p = document.createElement("p");
        p.className = "todo-item";
    -   p.innerText = inputText;
    +   p.innerText = todo;
    ~~~
1. 既存の`onClick`イベントに共通化した関数を入れる
    ~~~diff_JavaScript:index.js
    const onClickAdd = () => {
      // テキストボックスの値を取得し、初期化する
      const inputText = document.getElementById("add-text").value;
      document.getElementById("add-text").value = "";
    
    +   // 未完了リストに追加
    +   createIncompleteTodo(inputText);
    };
    ~~~
# 参考リンク
- []()