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

JavaScript
スポンサーリンク

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

6日目のテーマは、
「同じ“追加・一覧表示・全消去”なのに、中身の設計レベルを一段上げること」です。

機能の表向きは変わりません。

追加
一覧表示
全消去

でも今日は、コードの中身をこう育てます。

  • タスクに「ID」を持たせて、“ひとつひとつを区別できる”状態にする
  • 配列への「動的追加」が、ID や他の情報も含めて扱えることを体感する
  • 再描画が「どんな情報を持っていても、冷静に画面を組み立てる“土台”」になっていると理解する

まだ「1件削除」などの機能は付けません。
でも、「ID を持つタスク配列」を作ることで、明日からの拡張に耐えられる ToDo に進化させます。


いまの ToDo を「ID なしの世界」として振り返る

現在のタスクの形

5日目まで、タスクはこんな形でした。

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

text だけを持ったシンプルなオブジェクト。
これでも「追加・一覧表示・全消去」は問題なくできます。

でも、いざ「このタスクだけ削除したい」「このタスクを完了にしたい」となったときに、

「どのタスクを指しているのか」を判定しにくくなります。

同じ文字列のタスクが複数あったら?
真ん中の「勉強する」だけ消したいときは?

こういうときに必要になるのが ID です。


タスクに ID を持たせる理由とイメージ

なぜ ID が必要になるのか

例えばタスクがこうだったとします。

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

「上の“買い物に行く”だけ消したい」と思ったとき、
text だけでは区別がつきません。

そこで、こういう形にします。

[
  { id: 1, text: "買い物に行く" },
  { id: 2, text: "勉強する" },
  { id: 3, text: "買い物に行く" }
]
JavaScript

これなら、

  • id が 1 のタスクだけ
  • id が 2 のタスクだけ

というように、「どれを触るか」をハッキリ指定できます。

6日目では、まだ「ID を使った削除」はやりません。
でも 「タスクが ID を持っている状態」 を作ることが、明日からの一歩の準備になります。


ID 付きタスクを作るための「次の番号」を管理する

nextId という「カウンター」を用意する

タスクを追加するたびに、
自動で 1, 2, 3, … と増えていく ID がほしいです。

そこで、こういう変数を用意します。

let nextId = 1;
JavaScript

新しいタスクを作るときに、

const todo = {
  id: nextId,
  text: text,
};
nextId++;
JavaScript

という形で、

  • 今の nextId の値を id に使う
  • すぐ nextId を 1 増やしておく

というふうに管理します。

これで、

1回目の追加 → id: 1
2回目の追加 → id: 2
3回目の追加 → id: 3

と、自動で“かぶらない番号” が振られていきます。

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

「ID を自動採番するために、“変数をカウンターとして使う”」という発想です。


handleAdd を「ID 付きタスク対応」に書き換える

いまの handleAdd を ID 対応版に変える

元の handleAdd はこうでした。

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

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

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

これを、ID 付きに変えます。

let todos = [];
let nextId = 1;

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

  const todo = {
    id: nextId,
    text: text,
  };
  nextId++;

  todos.push(todo);

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

やっていることはこうです。

入力欄から文字列を取る。
空なら弾く。
{ id: nextId, text } というタスクオブジェクトを作る。
nextId を 1 増やしておく(次の準備)。
配列に push。
入力欄を空にして、render で再描画。

大事なのは、「ID の付与も“追加の一部”として自然に組み込まれている」ことです。
余計なことをしているように見えて、
実はこの ID が、これからの機能追加の“生命線”になります。


再描画は「ID を持っていても冷静」なままにする

createTodoElement で ID を「見えるように」してみる

ID を付けたので、表示上でも少しだけ「ID が存在する」ことを感じてみましょう。

今はこうでした。

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

これを、ID も表示する形にしてみます。

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

例えば、3つ追加すると画面にはこんな感じで出ます。

#1: 買い物に行く
#2: 勉強する
#3: 散歩する

これで、

配列の中
ブラウザの画面

の両方で、「タスクにはIDがある」という感覚が持てます。

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

「再描画の全体の流れは一切変えていない」 ことです。

render 自体は前と同じです。

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

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

配列の中身に ID が増えても、
render は「配列 → 画面」の変換役として、そのまま機能します。


全消去と nextId の扱いをどうするか考えてみる

全消去は「配列だけ」消すのか、「IDカウンター」もリセットするのか

handleClear は今こうなっています。

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

ここで考えたいのが、「nextId をどうするか」です。

選択肢は2つあります。

  1. nextId はそのまま(ID は増え続ける)
  2. nextId も 1 に戻す

どちらも間違いではありません。
それぞれの意味を考えてみます。

パターン1: nextId はそのまま

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

この場合、「過去に使ったIDは再利用しない」というポリシーになります。

1〜5まで使って全消去 → 次に追加したタスクは id: 6。

このほうが、「IDは一度使ったら二度と使わない」という意味で 一意性が強い です。
現実のアプリでも、このパターンはよく使われます。

パターン2: nextId もリセット

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

この場合、「画面をリセットしたらIDもリセットされる」動きになります。

「全部消したし、IDもリセットで気持ちよくゼロから」という感覚に近いです。

どちらを選ぶかは設計の好みですが、
6日目では「IDは使い捨てでかまわない」として パターン1(残す) で行くのをおすすめします。

大事なのは、「何となく」ではなく
「自分なりの理由を持って選択する」 ことです。


6日目の全体コードのイメージ

ID を追加したうえでの全体像を、ざっくりまとめます。

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 = [];
  // nextId はリセットしない設計にしてみる
  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

今日のポイントは、

配列 → 「ID付きタスクの配列」になった
動的追加 → 「IDも含めたタスクオブジェクト」を push するようになった
再描画 → 「IDが増えてもブレない設計」のまま動いている

という3つの一貫性です。


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

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

タスクに id を持たせて、「同じ text でも別のタスクとして区別できる」状態にした。
nextId というカウンター変数を使って、「追加のたびに自動で一意なIDを割り当てる」仕組みを作った。
handleAdd の中で、「入力値 → タスクオブジェクト → 配列に追加 → 再描画」という流れを、ID込みで組み立てた。
createTodoElement を少し変えて ID を表示し、「配列の中のIDと画面の見た目がつながっている」感覚を持てた。
全消去時に nextId をどうするかを考えることで、「コード上の選択に理由を持つ」という設計の視点に触れた。

7日目は、ここまで作ってきた ToDo アプリの設計を、
「自分の言葉で説明できる」レベルに整理する日 になります。

配列で何を持っているのか。
動的追加は何をどう変えているのか。
再描画は何を前提にして動いているのか。
ID を持たせたことで何ができるようになったのか。

これらを説明できるようになれば、
もう「簡単 ToDo アプリ 初級編」は、きちんと卒業です。

最後にひとつだけ。

今日の中で、「あ、これアプリっぽくなってきたな」と感じたのはどこでしたか?
#1: 買い物に行く のように ID がついて並んだ瞬間か、
「ID をどう扱うか」を自分で選べたところか。

その「アプリっぽさ」を感じた場所が、
あなたと“実践的なコード設計”をつなぐポイントです。
その感覚を持ったまま、7日目の総仕上げに進んでいきましょう。

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