7日目のゴールと今日のテーマ
7日目は、「簡単 ToDo アプリのコードを、自分の中で“設計図レベル”まで理解する日」です。
新しい機能(1件削除や完了チェック)を足す前に、
配列
動的追加
再描画
これがどうつながっているのかを、きちんと自分の言葉で説明できるようになるところを目指します。
終わるころには、
「シンプルな ToDo アプリなら、自分で一から組める気がする」
という感覚まで持っていきます。
ここまでで作ってきた ToDo アプリの“全体像”を確認する
コード全体を一枚の設計図として眺める
まずは、7日目時点の完成形に近いコードをざっと見ておきます。
const inputEl = document.getElementById("todo-input");
const addButton = document.getElementById("add-button");
const clearButton = document.getElementById("clear-button");
const listEl = document.getElementById("todo-list");
let todos = [];
let nextId = 1;
function createTodoElement(todo) {
const itemEl = document.createElement("div");
itemEl.textContent = `#${todo.id}: ${todo.text}`;
return itemEl;
}
function render() {
listEl.textContent = "";
todos.forEach((todo) => {
const itemEl = createTodoElement(todo);
listEl.appendChild(itemEl);
});
}
function handleAdd() {
const text = inputEl.value.trim();
if (text === "") {
return;
}
const todo = {
id: nextId,
text: text,
};
nextId++;
todos.push(todo);
inputEl.value = "";
render();
}
function handleClear() {
todos = [];
render();
}
function handleKeyDown(event) {
if (event.key === "Enter") {
handleAdd();
}
}
function setup() {
addButton.addEventListener("click", handleAdd);
clearButton.addEventListener("click", handleClear);
inputEl.addEventListener("keydown", handleKeyDown);
render();
}
setup();
JavaScriptこれを今日は、
「何となく分かる」から「一行一行の役割を説明できる」に変えていきます。
配列:ToDo アプリにとっての「真実の状態」
todos は「画面の元になる記憶」
todos はこう定義されていました。
let todos = [];
JavaScriptここに入る中身は、7日目時点では次のような形です。
[
{ id: 1, text: "買い物に行く" },
{ id: 2, text: "勉強する" },
{ id: 3, text: "散歩する" }
]
JavaScriptここで大事なのは、
画面に表示されている「ToDo の一覧」は、この配列の中身をそのまま映したものだということです。
配列が空なら、画面も空。
配列に3件あれば、画面にも3行並ぶ。
「今どんなタスクがあるか」の本体は、DOM(画面)ではなく、配列 todos 側にあります。
この「状態の本体を配列に置く」という発想が、アプリ設計の土台になっています。
タスク1件はオブジェクトで表現される
タスク1件は、次のようなオブジェクトです。
{
id: 1,
text: "買い物に行く"
}
JavaScripttext だけでなく、id を持つようにしたことで、
「どのタスクか」をはっきり区別できるようになっています。
同じ text のタスクが複数あっても、id が違えば別物として扱えます。
これは、今後
「このタスクだけ削除したい」
「このタスクだけ完了にしたい」
といった機能を作るときの準備になっています。
動的追加:入力から「ID付きタスク」が配列に入るまで
handleAdd の中で何が起きているのか
handleAdd をもう一度見てみます。
function handleAdd() {
const text = inputEl.value.trim();
if (text === "") {
return;
}
const todo = {
id: nextId,
text: text,
};
nextId++;
todos.push(todo);
inputEl.value = "";
render();
}
JavaScriptこれを順番に、日本語で分解してみます。
最初に、入力欄から文字列を取り出しています。
const text = inputEl.value.trim();
JavaScripttrim() を使って、前後の空白を削っています。
空白だけの入力(” ” のようなもの)を“実質空”として扱うためです。
次に、入力が空なら何もせず終わります。
if (text === "") {
return;
}
JavaScriptToDo として意味のあるものだけを配列に入れるための簡単な入力チェックです。
ここから、タスクオブジェクトを組み立てます。
const todo = {
id: nextId,
text: text,
};
nextId++;
JavaScript現在の nextId を ID として使い、
すぐに nextId を 1 増やすことで、
次のタスクには別の ID を振れるようにしています。
そして、配列に追加します。
todos.push(todo);
JavaScriptこの時点で、「アプリの状態」が変わりました。
タスクが1件増えた、ということです。
そのあと、入力欄を空にして、再描画します。
inputEl.value = "";
render();
JavaScriptここでの大事なポイントは、
「配列を変える」のと「画面を変える」のを、はっきり分けていることです。
配列の更新
→ todos.push(todo)
画面の更新
→ render()
このセットが「動的追加」の本質です。
再描画:配列から画面を“洗い替え”する仕組み
render がやっていることと、あえてやっていないこと
render はこうでした。
function render() {
listEl.textContent = "";
todos.forEach((todo) => {
const itemEl = createTodoElement(todo);
listEl.appendChild(itemEl);
});
}
JavaScript最初に、表示領域を空にしています。
listEl.textContent = "";
JavaScriptこの一行で、以前の ToDo 表示をすべて消します。
そのうえで、現在の todos の中身だけを使って、
改めて一覧を作り直します。
次に、todos をループしながら、1件ずつ要素を作っています。
todos.forEach((todo) => {
const itemEl = createTodoElement(todo);
listEl.appendChild(itemEl);
});
JavaScript1件分の見た目は、createTodoElement に任せています。
function createTodoElement(todo) {
const itemEl = document.createElement("div");
itemEl.textContent = `#${todo.id}: ${todo.text}`;
return itemEl;
}
JavaScriptここでの重要ポイントは、render は「配列を読むだけ」で、配列は書き換えないことです。
入力欄にも触りませんし、ボタンのイベントも変えません。
render を呼ぶと必ず起こるのは、「画面が配列と同じ状態にリセットされる」こと。
それ以外は何も起きません。
だからこそ、handleAdd から呼んでも、handleClear から呼んでも、
意味がぶれない“安心して呼べる関数”になっています。
全消去:配列をリセットして、render に任せる
handleClear の役割はとてもシンプル
全消去はこう書いていました。
function handleClear() {
todos = [];
render();
}
JavaScriptやっていることは、たった二つです。
配列を空にする。
空の配列をもとに、画面を描き直す。
画面上の要素を「一つずつ消す」のではなく、
「状態(配列)を空にして、あとは render に任せる」という設計です。
この書き方の気持ちよさは、
「画面と状態をあわせる仕事は、常に render が一括でやる」
と決めている点にあります。
どんな操作をしても最終的に render を通す、
という“一本のルール”があると、コードの迷子になりにくいです。
イベントとロジックの分離:入口は複数、本体は一つ
handleKeyDown がやっているのは「入口を増やす」だけ
Enter キーで追加できるようにした部分を見ます。
function handleKeyDown(event) {
if (event.key === "Enter") {
handleAdd();
}
}
JavaScriptこれがやっているのは、
「Enter キーが押されたら handleAdd を呼ぶ」
という“きっかけ作り”だけです。
ボタンのクリックも同じです。
addButton.addEventListener("click", handleAdd);
JavaScriptクリックでも Enter でも、最終的には handleAdd に集まります。
ここでのポイントは、
「動作の本体(タスク追加ロジック)は handleAdd の中だけにある」ということです。
イベント
→ 何が起きたか(クリック、キー入力)を受け取るだけ
handleAdd
→ 本当に配列を変えて、画面を作り直す
この分離があると、
今後イベントが増えても、
「本体ロジックはどこだっけ?」がはっきりしています。
setup 関数:アプリの“スタート地点”を一目で見せる
setup で「起動時にやること」をまとめる
setup はこうなっていました。
function setup() {
addButton.addEventListener("click", handleAdd);
clearButton.addEventListener("click", handleClear);
inputEl.addEventListener("keydown", handleKeyDown);
render();
}
setup();
JavaScriptsetup の役割は、
「アプリが動き始めるときに、一度だけやること」を集めることです。
イベント登録をし、
最初の render() を呼んで、
空の ToDo リストを画面に描きます。
ファイルの一番最後に setup(); と書かれていることで、
「ここからアプリが始まるんだな」という“入口”が目で見えるようになっています。
これもまた、設計としての優しさです。
7日目のミニワーク:自分の言葉で説明してみる
ここからは、あなた自身の頭の中でやってみてほしいことです。
問いを三つだけ投げます。
一つ目。
「この ToDo アプリで、“本当の状態”はどこにありますか?」
配列なのか、画面なのか。
その理由もふくめて、自分の言葉で言ってみてください。
二つ目。
「タスクが1つ追加されるとき、どの順番で何が起きていますか?」
入力
チェック
タスクオブジェクト作成
ID の付与
配列への追加
再描画
これを、自分の言葉でなぞってみてください。
三つ目。
「render を呼んだとき、必ず起きることと、絶対起きないことは何ですか?」
画面の一覧が配列にそろうことは起きる。
配列が勝手に変わることは起きない。
入力欄が消えることは起きない。
この三つが、自分の中でスッと言語化できたなら、
あなたはもう「簡単 ToDo アプリの設計を理解している側」です。
7日目のまとめと、これからの一歩
7日目でやったのは、
新しいトリックではなく、
配列
動的追加
再描画
この三つの関係を、設計目線で結び直すことでした。
配列 todos を「アプリの真実の状態」として見られるようになった。
動的追加を、「配列を変えてから render で画面に反映する」流れとして、順番ごとに理解できるようになった。
再描画 render を、「配列を読むだけの安全な関数」として設計できている意味をつかんだ。
イベント処理は“きっかけ”で、本体ロジックは別関数にまとめる、という分離の感覚を持てた。
ID 付きタスクを配列で扱いながらも、設計の芯は変えないまま保てた。
この7日間で身につけたのは、
「ToDo アプリの作り方」だけではありません。
配列で状態を持つ考え方。
イベントとロジックを分ける考え方。
再描画で画面を整える考え方。
これらは、これから作るどんな小さな Web アプリにも、そのまま使えます。
最後にひとつだけ、真面目に聞きたい。
この ToDo の型を、
あなたはどこで使ってみたいですか。
自分の勉強記録アプリでもいいし、
やりたいことリストでもいいし、
ゲーム内の「クエスト一覧」でもいい。
「ここならこの ToDo 使えるな」と思えた瞬間、
それはもう“練習用サンプル”ではなく、
あなた自身のアプリの設計になります。

