[JavaScript][L5] ボタン押下によりコンテンツ(div>ul>li)を別コンテンツ(div>ul>li)に移動(生成)する ([L4]で作成したコンポーネントを活用)

# 実施条件
[[JavaScript][L4] コンテンツ(div>ul>li)の生成機能を共通化(コンポーネント化)する](https://qiita.com/Jochun/items/c79fa73fb224d52897c0)を理解していること

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

# ソースコード外観
## 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
~~~jsx: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 = "戻す";
    backButton.addEventListener("click", () => {
      // TODOの内容を取得し、未完了リストに追加
      const todoText = backButton.previousElementSibling.innerText;
      createIncompleteTodo(todoText);
      // 押された戻すボタンの親にあるliタグを削除
      backButton.closest("li").remove();
    });
    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. `戻す`ボタン(`backButton`)が押された際に呼び出す関数を用意する
    ~~~diff_jsx:index.js
      // 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 = "戻す";
    +   backButton.addEventListener("click", () => {
    +   });
        moveTarget.firstElementChild.appendChild(backButton);
        // 完了リストに移動
        document.getElementById("complete-list").appendChild(moveTarget);
      });
    ~~~
1. TODOの内容を取得し、未完了リストに追加する
`previousElementSibling`は`nextElementSibling`の逆で、HTMLの前（コードで上では上部）の要素を指す
    ~~~diff_jsx:index.js
      // 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 = "戻す";
        backButton.addEventListener("click", () => {
    +     // TODOの内容を取得し、未完了リストに追加
    +     const todoText = backButton.previousElementSibling.innerText;
    +     createIncompleteTodo(todoText);
        });
        moveTarget.firstElementChild.appendChild(backButton);
        // 完了リストに移動
        document.getElementById("complete-list").appendChild(moveTarget);
      });
    ~~~
1. 押された戻すボタンの親にある`li`タグを削除
    ~~~diff_jsx:index.js
      // 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 = "戻す";
        backButton.addEventListener("click", () => {
          // TODOの内容を取得し、未完了リストに追加
          const todoText = backButton.previousElementSibling.innerText;
          createIncompleteTodo(todoText);
    +     // 押された戻すボタンの親にあるliタグを削除
    +     backButton.closest("li").remove();
        });
        moveTarget.firstElementChild.appendChild(backButton);
        // 完了リストに移動
        document.getElementById("complete-list").appendChild(moveTarget);
      });
    ~~~

# Tips
`戻す` `button`タグから見ると、`p`タグが`previousElementSibling`
~~~html:index.html
        <li>
          <div class="list-row">
            <p class="todo-item">TODOでした</p>
            <button>戻す</button>
          </div>
        </li>
~~~

`完了` `button`タグから見ると、`削除` `button`タグが`nextElementSibling`
~~~html:index.html
        <li>
          <div class="list-row">
            <p class="todo-item">TODOです</p>
            <button>完了</button>
            <button>削除</button>
          </div>
        </li>
~~~

# 参考リンク
- [Element: nextElementSibling プロパティ](https://developer.mozilla.org/ja/docs/Web/API/Element/nextElementSibling)
- [Element: previousElementSibling プロパティ](https://developer.mozilla.org/ja/docs/Web/API/Element/previousElementSibling)