JavaScript | 1 日 90 分 × 7 日アプリ学習:簡単 ToDo アプリ(初級編)

JavaScript
スポンサーリンク

5日目のゴールと今日のテーマ

5日目のテーマは、「まだ機能は同じなのに、“配列・動的追加・再描画”の理解を一段ガッチリ固めること」です。

機能としては変わりません。

追加
一覧表示
全消去

でも今日は、こういうところを狙います。

  • 「配列の中身」を意識的に観察して、頭の中のイメージと結びつける
  • 動的追加の中で、何がいつ変わっているのかをはっきりさせる
  • 再描画が“ただのおまけ”ではなく、“アプリの中枢”だと理解する

新しい UI を増やすというより、
コードと頭の中のイメージをぴったり重ねる日にします。


ここまでの ToDo アプリの“今のかたち”をもう一度整理する

5日目スタート時点のイメージ

だいたい、こんな形になっているはずです。

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 = [];

function createTodoElement(todo) {
  const itemEl = document.createElement("div");
  itemEl.textContent = 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 = { text: text };
  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

ここに「配列」「動的追加」「再描画」が全部きれいに並んでいます。
5日目では、これを“ただ暗記する”のではなく、一行一行の意味を自分の言葉で説明できるようにしていきます。


配列を「アプリの記憶」として“見える化”する

todos の中身を意識的に観察する

まず、配列 todos がどんなふうに変わっているかを、
頭の中だけじゃなく「ログ」で見てみましょう。

handleAdd に、ログを足します。

function handleAdd() {
  const text = inputEl.value.trim();
  if (text === "") {
    return;
  }

  const todo = { text: text };
  todos.push(todo);

  console.log("追加後の todos:", todos);

  inputEl.value = "";
  render();
}
JavaScript

これで、ブラウザのコンソールに
タスクを追加するたびに todos の中身が表示されます。

例えば、

1回目:「買い物に行く」を追加
[ { text: "買い物に行く" } ]

2回目:「勉強する」を追加
[ { text: "買い物に行く" }, { text: "勉強する" } ]

3回目:「散歩する」を追加
[ { text: "買い物に行く" }, { text: "勉強する" }, { text: "散歩する" } ]

このログを見ながら、

「画面に見えている“3行のリスト”の正体は、この配列だ」

と意識して眺めてみてください。

配列が“抽象的な概念”じゃなくて、
「コンソールに映っている“生のデータ”」として見えるようになると、一気に親しみやすくなります。


動的追加の中で「何がいつ変わるか」を分解する

handleAdd を日本語に完全分解してみる

handleAdd の中で起きていることを、あえて細かく日本語にします。

function handleAdd() {
  const text = inputEl.value.trim();
  if (text === "") {
    return;
  }

  const todo = { text: text };
  todos.push(todo);

  inputEl.value = "";
  render();
}
JavaScript

順番に追うと、

  1. 入力欄から文字列を取る(inputEl.value)。
  2. 前後の空白を削る(trim())。
  3. 空文字("")なら追加せずに終わる。
  4. { text: "入力された文字" } という タスク用オブジェクト を作る。
  5. todos.push(...) で配列の最後にそのオブジェクトを追加する。
  6. 入力欄を空にする(ユーザーに「追加されたよ」と伝わる小さなサイン)。
  7. render() を呼んで、今の配列の中身から画面を作り直す

ここで一番大事なのは、

「画面を変えるのは render、データを変えるのは handleAdd」

という分担です。

  • handleAdd がやっているのは「配列を変えること」
  • render がやっているのは「配列を元に画面を作ること」

この役割分担が、配列・動的追加・再描画をつなぐ“軸”になっています。


再描画を「アプリの中枢」として身体に落とし込む

render を“どこから呼んでも同じ意味”にしておく

render の中身をもう一度見てみます。

function render() {
  listEl.textContent = "";

  todos.forEach((todo) => {
    const itemEl = createTodoElement(todo);
    listEl.appendChild(itemEl);
  });
}
JavaScript

やっているのはこの2つだけです。

  1. listEl の中身を全部消す。
  2. todos の中身を先頭から順に読み、要素を作って全部追加する。

つまり、

「今の todos を、そのまま画面に映し直す」

これしかしていません。

だからこそ、

  • handleAdd から呼んでも
  • handleClear から呼んでも
  • 将来、何か別の場所から呼んでも

「画面を“正しい状態”に整え直す」という、同じ意味 になります。

ここで少し想像してみてください。

もし render の中で、

  • todos を書き換えていたり
  • input の中身を触ったり
  • 全然別のボタンの状態まで変えていたり

したらどうでしょう。

render を呼ぶたびに別のところまで勝手に変わる。
結果、「どこで何が起きるか分からない」関数になってしまいます。

「render は view(見た目)だけを担当する」
この約束を守れているのは、かなりいい状態です。


小さな実験で「配列と再描画の関係」を体感する

実験①:手動で配列を書き換えてから render してみる

例えば、コンソールからこうしてみます。

タスクを3つくらい追加したあと、ブラウザのコンソールにこう打ってみるイメージです。

todos.pop();  // 最後のタスクを1つ配列から削除
render();     // 再描画
JavaScript

すると、

  • 画面からも最後の1件が消えるはずです。

ここで感じてほしいのは、

「画面をいじらなくても、配列 → render だけで見た目が変わる」

ということです。

タスクを無理やりこう書き換えてみてもいいです。

todos = [
  { text: "手動で差し替えたタスク1" },
  { text: "手動で差し替えたタスク2" },
];
render();
JavaScript

すると、画面もその2件だけになります。

これが、

配列(状態)が「真実」。
render が「その真実を描画」。

という構造そのものです。


もう一段だけ設計を意識する:createTodoElement の意味

「1件分の見た目をカプセル化する」感覚

createTodoElement は、今はこれだけです。

function createTodoElement(todo) {
  const itemEl = document.createElement("div");
  itemEl.textContent = todo.text;
  return itemEl;
}
JavaScript

でも、ここに例えばクラス名を付けたり、
アイコンを付けたりするのは この関数だけ 触ればOKです。

function createTodoElement(todo) {
  const itemEl = document.createElement("div");
  itemEl.className = "todo-item";
  itemEl.textContent = "• " + todo.text;
  return itemEl;
}
JavaScript

すると、

  • 再描画のロジック(render)は一切変えなくていい
  • 配列の構造(todos)も一切変えなくていい
  • 見た目だけをこの関数でコントロールできる

という状態になります。

これが、「1件分の見た目をカプセル化する」感覚です。

配列:データのまとまり
render:データ全体 → 画面全体
createTodoElement:データ1件 → 画面1件

きれいに三層に分かれています。


5日目の「頭の中での整理」を言葉にしてみる

ここで一度、あなた自身の言葉で整理してほしいポイントがあります。

質問①:配列 todos は、あなたにとって何ですか?

単なる「リスト」でもいいし、
「アプリの記憶」でもいいし、
「ToDoリストの本体」でもいい。

自分の言葉で説明してみてください。

質問②:動的追加は、どんな順番で何をしていますか?

  • 値を取る
  • 空を弾く
  • オブジェクトにする
  • 配列に push
  • 入力欄を消す
  • render

この流れを、1つずつ自分の言葉で説明できると、
もう「なんとなく書いている」状態から卒業です。

質問③:render を呼んだときに“必ず起こること”と、“起こらないこと”は何ですか?

起こること:

  • 画面の一覧が配列 todos の中身に合わせて描き直される

起こらないこと:

  • todos は書き換えられない
  • 入力欄の中身は変わらない
  • ボタンのイベントは変わらない

この「やること / やらないこと」の境界が見えていると、
関数の設計が一段レベルアップします。


5日目のまとめと、6日目へのつなぎ

今日やったのは、新しい機能ではなく 「理解の解像度を上げること」 でした。

振り返ると、こんなことをやりました。

  • コンソールログを使って、配列 todos の中身と画面の対応を“目で見て”理解した。
  • handleAdd を細かく分解し、「どのタイミングで何が変わっているか」を言葉で追えるようにした。
  • render は「配列 → 画面」の変換に専念していて、“いつ呼んでも安全な関数”だと確認した。
  • コンソールから配列を書き換えて render() する実験で、「画面はあくまで配列の写し」という感覚を体でつかんだ。
  • createTodoElement が「タスク1件分の見た目」をカプセル化していることを意識し、役割の分離を感じた。

6日目以降は、いよいよ

  • 1件だけ削除する
  • 完了チェックを付ける
  • タスクに ID や done フラグを持たせる

といった「ToDoアプリらしい動き」に踏み込んでいきます。
ただ、そのとき土台になるのは、まさに今日整理した

「配列 → 動的追加 → 再描画」という一本の流れ

です。

最後にひとつ、あなたに聞きたい。

今日の中で、「あ、自分これもう怖くないかも」と感じたのはどこでしたか?
配列の中身をログで見た瞬間か、render を“何度呼んでもいいやつ”だと理解したところか。

その「怖くなさ」が増えた分だけ、
次に一歩踏み込む余裕が生まれています。
その余裕を持ったまま、6日目の“機能強化”に進んでいきましょう。

タイトルとURLをコピーしました