JavaScript | 1 日 60 分 × 7 日アプリ学習:TODOアプリ編

APP JavaScript
スポンサーリンク

この7日間 TODOアプリ学習のゴール

この 7 日間では、「ブラウザ上で動くシンプルな TODO アプリ」を完成させます。
機能は次の 4 つに絞ります。

  1. テキスト入力欄にやることを書く
  2. 追加ボタンでリストに追加
  3. 削除ボタンで消す
  4. チェックボックスで「完了済み」を切り替え、見た目を変える

HTML/CSS は最低限にして、「JavaScriptで画面を動かす」「状態を管理する」ことに集中します。


1 日目:環境準備と「画面の骨組み」を作る

ファイルとフォルダの準備

まずは作業用フォルダを作ります。

  1. デスクトップなどに todo-app フォルダを作成
  2. その中に index.html を作る
  3. index.html をブラウザで開けるようにしておく(ダブルクリックでOK)

TODOアプリの最低限のHTMLを書く

index.html に、まずは画面の骨組みを書きます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>TODO アプリ</title>
  <style>
    body {
      font-family: sans-serif;
      padding: 20px;
    }
    .container {
      max-width: 400px;
      margin: 0 auto;
    }
    .input-area {
      margin-bottom: 16px;
    }
    .input-area input {
      width: 70%;
      padding: 4px;
      font-size: 16px;
    }
    .input-area button {
      padding: 6px 10px;
      font-size: 14px;
      margin-left: 4px;
    }
    .todo-item {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 4px 0;
      border-bottom: 1px solid #ddd;
    }
    .todo-text {
      flex-grow: 1;
      margin-left: 8px;
    }
    .completed {
      text-decoration: line-through;
      color: #888;
    }
    .todo-actions button {
      margin-left: 4px;
      font-size: 12px;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>TODO アプリ</h1>

    <div class="input-area">
      <input id="todo-input" type="text" placeholder="やることを入力..." />
      <button id="add-button">追加</button>
    </div>

    <div id="todo-list">
      <!-- TODO 項目がここに追加される -->
    </div>
  </div>

  <script>
    console.log("TODO アプリスタート");
  </script>
</body>
</html>

ブラウザで開いて、

  • タイトル「TODO アプリ」
  • テキスト入力欄
  • 「追加」ボタン

が表示されていれば 1 日目はOKです。
まだ TODO を追加する機能はありませんが、これが「アプリの土台」です。


2 日目:DOM を触る感覚と「1つだけ TODO を追加」してみる

DOM を通して要素を取る

JavaScript から HTML の要素を触る基本は「id で取る」ことです。
<script> 内を次のように書き換えます。

<script>
  const input = document.getElementById("todo-input");
  const addButton = document.getElementById("add-button");
  const todoList = document.getElementById("todo-list");

  console.log(input);
  console.log(addButton);
  console.log(todoList);
</script>

ブラウザのデベロッパーツール(検証 → Console)を開くと、それぞれの要素が表示されているはずです。
これは「JavaScript側から HTML をちゃんと掴めているか」の確認です。

ボタンを押したら console.log する

まずは「イベントが動いているか」を確認します。

<script>
  const input = document.getElementById("todo-input");
  const addButton = document.getElementById("add-button");
  const todoList = document.getElementById("todo-list");

  addButton.addEventListener("click", function() {
    console.log("追加ボタンが押されました");
    console.log("入力されている値:", input.value);
  });
</script>

ブラウザで入力欄に「牛乳を買う」などと入れて「追加」を押すと、コンソールにその文字が表示されるはずです。
input.value が「入力された文字」を表します。

TODO の要素を JavaScriptで作ってみる(1件だけ)

最初は「1件だけ固定で追加」でもOKです。
addEventListener の中身を、次のように書いてみてください。

addButton.addEventListener("click", function() {
  const text = input.value;

  const todoItem = document.createElement("div");
  todoItem.className = "todo-item";

  todoItem.textContent = text;

  todoList.appendChild(todoItem);
});
JavaScript

ポイントは次の通りです。

  • document.createElement("div") で新しい <div> を作る
  • className でクラスを付けて、CSS のスタイルを適用する
  • appendChildtodo-list の中に追加する

この時点ではチェックボックスや削除ボタンはありません。まずは「入力したテキストが下に追加される」という感覚を掴みます。


3 日目:「1アイテムの構造」を設計し、チェックボックスと削除ボタンを付ける

TODO アイテムの構造を決める

1 つの TODO アイテムは、次のような構造にします。

  • チェックボックス
  • テキスト部分
  • 「削除」ボタン

HTML で書くとイメージはこうです(直接は書きませんが構造イメージ用)。

<div class="todo-item">
  <input type="checkbox" />
  <span class="todo-text">牛乳を買う</span>
  <div class="todo-actions">
    <button>削除</button>
  </div>
</div>

これを JavaScript で組み立てます。

1アイテムを作る関数を定義する(重要)

<script> の中に、アイテムを作る関数を書きます。

function createTodoItem(text) {
  const item = document.createElement("div");
  item.className = "todo-item";

  const checkbox = document.createElement("input");
  checkbox.type = "checkbox";

  const span = document.createElement("span");
  span.className = "todo-text";
  span.textContent = text;

  const actions = document.createElement("div");
  actions.className = "todo-actions";

  const deleteButton = document.createElement("button");
  deleteButton.textContent = "削除";

  actions.appendChild(deleteButton);

  item.appendChild(checkbox);
  item.appendChild(span);
  item.appendChild(actions);

  return item;
}
JavaScript

この関数は、「文字列を渡すと TODO アイテムの DOM を組み立てて返す」部品です。
こうやって「作る処理」を関数にまとめておくと、追加機能を入れるときに見通しが良くなります。

追加ボタンからこの関数を使う

addButton のクリック処理を次のように変えます。

addButton.addEventListener("click", function() {
  const text = input.value.trim();

  if (text === "") {
    return;
  }

  const todoItem = createTodoItem(text);
  todoList.appendChild(todoItem);

  input.value = "";
});
JavaScript

trim() は前後の空白を消すメソッドです。空文字のときは追加しないようにしています。
この時点で、「入力 → 追加 → TODO アイテムが表示される」ところまで行ければOKです。


4 日目:削除機能と「イベントリスナーの付け方」を深掘りする

削除ボタンにイベントを付ける

3 日目の createTodoItem 関数の中で、削除ボタンにイベントを付けます。

deleteButton.addEventListener("click", function() {
  todoList.removeChild(item);
});
JavaScript

最終的な createTodoItem はこうなります。

function createTodoItem(text) {
  const item = document.createElement("div");
  item.className = "todo-item";

  const checkbox = document.createElement("input");
  checkbox.type = "checkbox";

  const span = document.createElement("span");
  span.className = "todo-text";
  span.textContent = text;

  const actions = document.createElement("div");
  actions.className = "todo-actions";

  const deleteButton = document.createElement("button");
  deleteButton.textContent = "削除";

  deleteButton.addEventListener("click", function() {
    todoList.removeChild(item);
  });

  actions.appendChild(deleteButton);
  item.appendChild(checkbox);
  item.appendChild(span);
  item.appendChild(actions);

  return item;
}
JavaScript

ここで重要なのは、「イベントの中から外側の item を参照している」ことです。
このスコープ(範囲)の感覚は、今後必ず役立ちます。

深掘り:なぜ todoList.removeChild(item) なのか

DOM の構造は「親子関係」でできています。
todoList は TODO アイテムの「親」、item はその「子」です。

削除したいときは、「親から子を取り除く」という形で書きます。
だから

親要素.removeChild(子要素);
JavaScript

という書き方になるわけです。


5 日目:完了チェック(チェックボックスで見た目を変える)

完了済みTODOの見た目を決める

すでに CSS に .completed を書いてあります。

.completed {
  text-decoration: line-through;
  color: #888;
}

このクラスを付けたり外したりすることで、「完了済み」を表現します。
付いていれば「取り消し線+グレー」、付いていなければ普通のテキスト、というイメージです。

チェックボックスにイベントを付ける(重要)

createTodoItem 関数の中で、チェックボックスのイベントを追加します。

checkbox.addEventListener("change", function() {
  if (checkbox.checked) {
    span.classList.add("completed");
  } else {
    span.classList.remove("completed");
  }
});
JavaScript

ポイントは次の通りです。

  1. change イベントは「チェック状態が変わったとき」に発火する
  2. checkbox.checkedtrue ならチェック済み、false なら未チェック
  3. classList.add / classList.remove でクラスの付け外しを行う

これで、「チェックを入れると取り消し線が付き、外すと戻る」動きが作れます。

動きを確認する

  1. 何個か TODO を追加する
  2. 任意の TODO にチェックを入れたり外したりする
  3. 削除ボタンで消せるか試す

ここまで出来たら、TODO アプリとして最低限使える状態になっています。


6 日目:「見直し」と「キーボード操作」などの小さな改善

Enterキーで追加できるようにする

ユーザー体験として、「入力して Enter で追加」は欲しいところです。
input に対してキーイベントを付けます。

input.addEventListener("keydown", function(event) {
  if (event.key === "Enter") {
    addButton.click();
  }
});
JavaScript

ポイントは、

  • keydown でキーが押された瞬間をとらえる
  • event.key === "Enter" で Enter キーかどうか判定
  • addButton.click() で「追加ボタンが押されたことにする」

これで、「マウスを使わずキーボードだけでも追加できる」ようになります。

追加時のバリデーションを少しだけ強くする

すでに空文字は無視していますが、スペースだけの入力も避けたいので、trim() は重要です。
さらに、TODO が多すぎるのを防ぎたいなら、上限を設けてもよいです。

例として、最大 50 件までにするとします。

addButton.addEventListener("click", function() {
  const text = input.value.trim();

  if (text === "") {
    return;
  }

  if (todoList.children.length >= 50) {
    alert("TODO は 50 件までです");
    return;
  }

  const todoItem = createTodoItem(text);
  todoList.appendChild(todoItem);

  input.value = "";
});
JavaScript

ここでの学びは、「DOM の子要素の数を数える」というテクニックと、
「仕様(上限)を自分で決めて、それをコードに落とす」感覚です。

コードを少し整理する

例えば、「TODO を追加する処理」を関数にまとめると、コードの見通しが良くなります。

function addTodo() {
  const text = input.value.trim();

  if (text === "") {
    return;
  }

  const todoItem = createTodoItem(text);
  todoList.appendChild(todoItem);

  input.value = "";
}

addButton.addEventListener("click", addTodo);

input.addEventListener("keydown", function(event) {
  if (event.key === "Enter") {
    addTodo();
  }
});
JavaScript

同じ処理を二度書かずに、関数にして使い回す。
これはプロの現場でも非常に重要な習慣です。


7 日目:完成形のコードを通して読み、「自分の味付け」を加える

完成版の全体コード(シンプル版)

ここまでの内容を組み合わせた、1 つの完成例です。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>TODO アプリ</title>
  <style>
    body {
      font-family: sans-serif;
      padding: 20px;
      background-color: #f5f5f5;
    }
    .container {
      max-width: 400px;
      margin: 0 auto;
      background-color: #fff;
      border-radius: 8px;
      padding: 16px;
      box-shadow: 0 0 8px rgba(0,0,0,0.1);
    }
    h1 {
      text-align: center;
      margin-top: 0;
    }
    .input-area {
      margin-bottom: 16px;
      display: flex;
    }
    .input-area input {
      flex-grow: 1;
      padding: 6px;
      font-size: 16px;
    }
    .input-area button {
      padding: 6px 10px;
      font-size: 14px;
      margin-left: 4px;
    }
    #todo-list {
      margin-top: 8px;
    }
    .todo-item {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 4px 0;
      border-bottom: 1px solid #eee;
    }
    .todo-text {
      flex-grow: 1;
      margin-left: 8px;
      word-break: break-all;
    }
    .completed {
      text-decoration: line-through;
      color: #888;
    }
    .todo-actions button {
      margin-left: 4px;
      font-size: 12px;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>TODO アプリ</h1>

    <div class="input-area">
      <input id="todo-input" type="text" placeholder="やることを入力..." />
      <button id="add-button">追加</button>
    </div>

    <div id="todo-list"></div>
  </div>

  <script>
    const input = document.getElementById("todo-input");
    const addButton = document.getElementById("add-button");
    const todoList = document.getElementById("todo-list");

    function createTodoItem(text) {
      const item = document.createElement("div");
      item.className = "todo-item";

      const checkbox = document.createElement("input");
      checkbox.type = "checkbox";

      const span = document.createElement("span");
      span.className = "todo-text";
      span.textContent = text;

      const actions = document.createElement("div");
      actions.className = "todo-actions";

      const deleteButton = document.createElement("button");
      deleteButton.textContent = "削除";

      checkbox.addEventListener("change", function() {
        if (checkbox.checked) {
          span.classList.add("completed");
        } else {
          span.classList.remove("completed");
        }
      });

      deleteButton.addEventListener("click", function() {
        todoList.removeChild(item);
      });

      actions.appendChild(deleteButton);
      item.appendChild(checkbox);
      item.appendChild(span);
      item.appendChild(actions);

      return item;
    }

    function addTodo() {
      const text = input.value.trim();

      if (text === "") {
        return;
      }

      const todoItem = createTodoItem(text);
      todoList.appendChild(todoItem);

      input.value = "";
      input.focus();
    }

    addButton.addEventListener("click", addTodo);

    input.addEventListener("keydown", function(event) {
      if (event.key === "Enter") {
        addTodo();
      }
    });
  </script>
</body>
</html>
HTML

細かい書き方は変えても構いません。
重要なのは、「入力 → 追加 → 完了チェック → 削除」の流れを、
DOM 操作とイベント、そして状態の変化として理解できていることです。

自分のアレンジを入れてみるアイデア

ここから先は「遊び」です。例えば次のような改良が考えられます。

  1. 「完了済みだけを非表示にする」チェックボックスを追加
  2. 「すべて削除」ボタンを追加
  3. TODO をブラウザのローカルストレージに保存して、ページをリロードしても残るようにする
  4. 優先度(高・中・低)をドロップダウンで選べるようにする

どれも難しそうに見えますが、実は今学んだ DOM とイベント、変数の管理を応用しているだけです。


このTODOアプリで身につく「本物の基礎」

この 7 日間で、ただ TODO アプリを作っただけではありません。
あなたは次の感覚をちゃんと身につけています。

  • HTML の「部品」と JavaScript の「ロジック」を、DOM を通じてつなぐ感覚
  • ボタンや入力欄、チェックボックスなどに「イベントで動きを付ける」方法
  • 1 つのTODOアイテムを「構造設計 → 関数化 → 再利用」という流れで組み立てる考え方
  • 少しずつ仕様を足しながら、アプリを育てていくプロセス

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