Day25 後半のゴール
前半で「タスクを入力して、画面に追加する」ところまではできました。
後半ではそこから一歩進めて、
タスクを配列で管理する
「画面」と「データ」を分けて考える
後で削除や完了機能を足しやすい形にする
ここを丁寧に作っていきます。
いきなり高機能にはしないけれど、「設計として強い TODO」に育てていきます。
なぜ配列でタスクを管理するのか
画面だけをいじると、すぐ限界が来る
前半のコードは、ボタンを押すたびに p 要素を作って、そのまま画面に追加していました。
これは「とりあえず動かす」には十分ですが、次のようなことをやろうとすると困ります。
特定のタスクだけ削除したい
完了したタスクだけ見た目を変えたい
後で localStorage に保存したい
なぜ困るかというと、「タスクの本当の姿」が DOM の中にしかないからです。
どのタスクが何番目なのか、どんな状態なのか、JavaScript 側で把握しづらい。
そこで、「タスクの本体は配列で持つ」「画面はその写し」という形に変えます。
タスク配列を導入する
タスクの「リスト」をコードの中に持つ
まず、タスクを入れておく配列を用意します。
const taskInputElement = document.getElementById("taskInput");
const addButtonElement = document.getElementById("addButton");
const taskListElement = document.getElementById("taskList");
let tasks = [];
JavaScriptここで tasks は、「タスクの一覧」を表す配列です。
最初は空ですが、タスクを追加するたびに中身が増えていきます。
この時点で、目線を少し変えます。
画面にある p 要素たちが「真実」ではない
tasks 配列が「真実」で、画面はそのコピー
という考え方に切り替えます。
配列から画面を描画する関数を作る
renderTasks という「出力担当」を作る
配列を導入したら、次にやるのは「配列の中身を画面に反映する」関数づくりです。
function renderTasks() {
taskListElement.textContent = "";
tasks.forEach((text) => {
const taskElement = document.createElement("p");
taskElement.textContent = text;
taskListElement.appendChild(taskElement);
});
}
JavaScriptここでやっていることはシンプルです。
一度、一覧を空にする
tasks の中身を順番に取り出して p 要素を作る
それを taskListElement に追加する
これで、「tasks 配列さえ正しければ、画面はいつでも再構築できる」状態になります。
この構造が、後で効いてきます。
追加処理を「配列+描画」に書き換える
直接 DOM をいじるのではなく、配列を更新する
前半では、クリック時に直接 p 要素を作っていました。
後半では、「配列を更新 → 画面を再描画」という流れに変えます。
addButtonElement.addEventListener("click", () => {
const text = taskInputElement.value.trim();
if (text === "") {
return;
}
tasks.push(text);
renderTasks();
taskInputElement.value = "";
});
JavaScriptここでの流れはこうです。
入力欄からテキストを取得する
空なら何もしない
tasks 配列に追加する
renderTasks で画面を更新する
この形にしておくと、例えば「削除」機能を付けたいときも、
tasks から該当の要素を消す
renderTasks を呼ぶ
だけで済みます。
画面を直接いじり回さなくてよくなるのが、配列管理の強さです。
関数分割で役割をさらに整理する
addTask と renderTasks に分ける
今のコードでも十分動きますが、もう一歩だけ整理してみます。
「タスクを追加する」という処理を関数に切り出します。
function addTask(text) {
tasks.push(text);
}
JavaScriptイベントハンドラはこう書き換えられます。
addButtonElement.addEventListener("click", () => {
const text = taskInputElement.value.trim();
if (text === "") {
return;
}
addTask(text);
renderTasks();
taskInputElement.value = "";
});
JavaScriptこれで、役割がはっきりします。
addTask
「データ(tasks 配列)を更新する」担当
renderTasks
「画面(DOM)を更新する」担当
イベントハンドラ
「入力を受け取り、処理の流れを組み立てる」担当
この分け方は、アプリが大きくなっても通用する設計の基本です。
例題:削除機能を見据えた設計のイメージ
今は実装しないけれど、頭の中でシミュレーションしてみる
今は「タスク追加」だけですが、
この先ほぼ確実に「削除したい」「完了にしたい」という欲が出てきます。
そのときのことを、少しだけイメージしてみましょう。
削除したいタスクを特定するには、
tasks の「何番目か」が分かっていると便利です。
renderTasks の中で index を使えば、それも簡単に扱えます。
function renderTasks() {
taskListElement.textContent = "";
tasks.forEach((text, index) => {
const taskElement = document.createElement("p");
taskElement.textContent = text;
taskListElement.appendChild(taskElement);
});
}
JavaScriptもし削除ボタンを付けるなら、index を使って
tasks.splice(index, 1);
renderTasks();
という流れにできます。
ここで大事なのは、「配列が真実」という設計にしておくと、
こういう拡張が自然にできる、という感覚です。
デバッグ視点で配列と描画を確認する
「配列の中身」と「画面の状態」を意識して見る
もし、
タスクを追加しても表示されない
なぜか同じタスクが増え続ける
といったことが起きたら、次のように console.log を入れてみてください。
function addTask(text) {
tasks.push(text);
console.log("現在の tasks:", tasks);
}
function renderTasks() {
console.log("renderTasks を実行します");
taskListElement.textContent = "";
tasks.forEach((text, index) => {
console.log("描画するタスク:", index, text);
const taskElement = document.createElement("p");
taskElement.textContent = text;
taskListElement.appendChild(taskElement);
});
}
JavaScriptこれで、
クリックのたびに tasks がどう変わっているか
renderTasks がちゃんと呼ばれているか
forEach でどんな値が回ってきているか
を一つずつ確認できます。
「配列の状態」と「画面の状態」を分けて意識できるようになると、
バグの原因も見つけやすくなります。
Day25 後半の完成コード
配列管理まで含めた TODO①
ここまでの内容をまとめると、こうなります。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>TODOアプリ</title>
</head>
<body>
<h1>TODOアプリ</h1>
<h2>タスク追加</h2>
<input id="taskInput" type="text" placeholder="やることを入力">
<button id="addButton">追加</button>
<h2>タスク一覧</h2>
<div id="taskList"></div>
<script>
const taskInputElement = document.getElementById("taskInput");
const addButtonElement = document.getElementById("addButton");
const taskListElement = document.getElementById("taskList");
let tasks = [];
function addTask(text) {
tasks.push(text);
}
function renderTasks() {
taskListElement.textContent = "";
tasks.forEach((text) => {
const taskElement = document.createElement("p");
taskElement.textContent = text;
taskListElement.appendChild(taskElement);
});
}
addButtonElement.addEventListener("click", () => {
const text = taskInputElement.value.trim();
if (text === "") {
return;
}
addTask(text);
renderTasks();
taskInputElement.value = "";
});
</script>
</body>
</html>
見た目は前半とあまり変わりません。
でも中身は、
タスクを配列で管理している
画面は配列から再描画している
追加処理と描画処理が関数で分かれている
という、かなり「アプリらしい」構造になっています。
Day25 後半のまとめ
後半で押さえたポイントは、次のようなものです。
タスクを配列(tasks)で管理するようにした
配列から画面を描画する renderTasks を作った
タスク追加処理を addTask に分けて、役割を整理した
「配列が真実、画面はその写し」という設計に切り替えた
デバッグするときは「配列の中身」と「描画の流れ」を分けて見る
この形まで来ると、
次のステップである「削除」「完了」「保存(localStorage)」が、
無理なく積み上げられるようになります。
ここから先は、もう「自分の TODO をどう育てたいか」という世界です。
その土台として、今日の設計はかなりいい線いってます。
