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

JavaScript
スポンサーリンク

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

2日目のテーマは、1日目で作った ToDo アプリの

  • 配列での管理
  • 動的追加
  • 再描画

この3つを「よりちゃんとした設計」に育てることです。

機能としてはまだ同じです。

  • 追加
  • 一覧表示
  • 全消去

でも今日は、

  • 「1つ1つのタスク」をオブジェクトで扱う
  • 再描画の仕組みを“読めるコード”に整える
  • ちょっとした使いやすさ(Enterキーで追加など)も入れてみる

ここまで行けると、“簡単だけど設計された ToDo アプリ”に近づきます。


1日目の ToDo アプリを「設計目線」で振り返る

配列でタスクを管理していた

1日目では、タスクをこんな配列で持っていました。

let todos = []; // 中身は ["買い物に行く", "勉強する"] みたいな文字列の配列
JavaScript

追加するときはこうでした。

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

  todos.push(text);
  inputEl.value = "";
  render();
}
JavaScript

ここで大事だったのは、

「タスク一覧の“本体”は配列 todos
「画面に見えている文字は、そのコピーに過ぎない」

という考え方でした。

再描画で一覧を毎回作り直していた

1日目の render は、だいたいこうでした。

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

  todos.forEach((todo) => {
    const itemEl = document.createElement("div");
    itemEl.textContent = todo;
    listEl.appendChild(itemEl);
  });
}
JavaScript

配列を全部読み直して、画面を作り直す。
これが「再描画」の基本パターンでした。


2日目の一歩目:「タスク1件」をオブジェクトで表現する

文字列だけだと、すぐ限界が来る

今の todos はこういう状態です。

["買い物に行く", "勉強する"]

これでも動きますが、もし今後、

  • 作成日時を持たせたい
  • 完了フラグ(済/未)を持たせたい
  • IDを持たせて一意に扱いたい

と思ったとき、文字列だけでは足りません。

そこで、2日目では「タスク1件」をオブジェクトとして扱ってみます。

タスクオブジェクトの形を決める

まずはシンプルに、こういう形にします。

{
  text: "買い物に行く"
}
JavaScript

配列に入れると、こうなります。

let todos = [
  { text: "買い物に行く" },
  { text: "勉強する" }
];
JavaScript

追加するときは、文字列ではなく「オブジェクト」を push します。

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

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

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

ここでの重要ポイントは、

「タスク 1 件 = オブジェクト 1つ」
「タスク一覧 = オブジェクトの配列」

という構造に変えたことです。
これで、あとで「プロパティを増やす」ことが簡単になります。


タスクオブジェクトに合わせて再描画関数を直す

todo.text を使って表示する

さっき todos の中身をオブジェクトに変えたので、render も少し直します。

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

  todos.forEach((todo) => {
    const itemEl = document.createElement("div");
    itemEl.textContent = todo.text;
    listEl.appendChild(itemEl);
  });
}
JavaScript

これで、

  • todos:オブジェクトの配列
  • render:オブジェクトから text を取り出して表示

という形になりました。

今は text しかありませんが、
明日以降 iddone(完了フラグ)などを足しても、render の構造はそのまま使えます。


再描画を“読みやすく”するための分解

「タスク表示1個分」を関数にしてみる

render の中で、1つ1つのタスク要素を作っています。

const itemEl = document.createElement("div");
itemEl.textContent = todo.text;
listEl.appendChild(itemEl);
JavaScript

これを、小さな関数にしてみます。

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

render はこう書けます。

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

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

ここでの深掘りポイントは、

  • render が「流れ」だけを書いている
  • 「1件分の見た目」は createTodoElement に任せている

という役割分担です。

今はまだ見た目がシンプルですが、
明日以降「削除ボタンをつける」「チェックボックスをつける」などをしたとき、
“1件分の見た目を変えたいときは createTodoElement だけ見ればいい”
という状態になります。


ちょっとした使いやすさ:Enterキーで追加する

クリックだけでなく Enter でも追加したい

1日目は「追加ボタン」をクリックしてタスクを追加していました。
2日目では、入力欄で Enter を押したときにも追加できるようにします。

まず、入力欄にキーボードイベントを登録します。

inputEl.addEventListener("keydown", handleKeyDown);
JavaScript

handleKeyDown を書きます。

function handleKeyDown(event) {
  if (event.key === "Enter") {
    handleAdd();
  }
}
JavaScript

ここでのポイントは、

  • Enter が押されたときだけ、handleAdd を呼ぶ
  • 「追加の本体処理」はあくまで handleAdd に集約している

ということです。

「クリックでも Enter でも、やりたいことは同じ」なので、
処理の本体は1か所(handleAdd)にしておきます。
これも設計としてかなり大事な感覚です。


全消去の挙動も「配列 → 再描画」で徹底する

配列を空にしてから render

全消去ボタンの扱いは、1日目と同じです。

clearButton.addEventListener("click", handleClear);

function handleClear() {
  todos = [];
  render();
}
JavaScript

ここで改めて強調したいのは、

  • 画面から要素を探して逐一消す、ということはしない
  • あくまで「配列を状態の真実」として扱い、画面はそれを映しているだけ

という一点です。

2日目でタスクをオブジェクトに変えても、
「全消去の考え方」が一切変わっていないことに注目してほしいです。
これが「設計をちゃんと持っている状態」です。


2日目の全体イメージ(コードの骨組み)

ここまでをまとめると、だいたいこうなります。

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

ここまで来ると、

  • 配列 todos がアプリの「状態」を持ち
  • render が「状態 → 画面」を担当し
  • 各ハンドラ(handleAdd / handleClear / handleKeyDown)が「状態の変化」を担当する

という、きれいな三分割になっています。


2日目のまとめと、3日目へのつなぎ

今日やったことを、言葉で整理します。

  • タスク1件を文字列ではなくオブジェクト { text: "…" } として表現した
  • タスク一覧を「オブジェクトの配列」として管理し、将来の拡張に耐えられる形にした
  • createTodoElement を導入して、1件分の見た目と render の流れを分離した
  • Enterキーでの追加を handleKeyDownhandleAdd という形で実装し、「処理の本体は1か所」に集約する感覚をつかんだ
  • 「全消去 = 配列を空にして再描画」というシンプルな設計を、オブジェクト化後も維持した

3日目以降は、ここに

  • 1件だけ削除する
  • 完了状態(チェック)を持たせる
  • IDでタスクを特定する

といった「ToDoっぽさ」を少しずつ足していきます。

最後に一つだけ。

今日の中で、「あ、これちょっと設計として気持ちいいな」と感じたのはどこでしたか?
タスクを { text } のオブジェクトにしたところか、
createTodoElement で“1件分の見た目”を切り出したところか、
Enter とクリックの両方が同じ handleAdd に集まってきたところか。

その「気持ちいい」と感じた箇所が、
あなたのプログラム設計のセンスと一番強くつながっている場所です。
そこを意識しながら、3日目の ToDo 強化に進んでいきましょう。

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