JavaScript: 総合プロジェクト:Todo アプリ

最後のレッスンにおめでとうございます!これまでの27レッスンで、変数、関数、配列、DOM、イベント、JSON、非同期プログラミング、クラスなどを学んできました。それらをすべて結びつけて、完全な小さなアプリケーションを構築する時が来ました。このプロジェクトは、学んだすべての集大成となります。


1. プロジェクト概要

Todo アプリを構築します。シンプルに見えますが、学んだほぼすべてのコアコンCEPTに触れます。完成すれば、実際に使える生産性ツールが手に入ります。



▶ サンプル

JAVASCRIPT
// セクションのコード例
const message = "Hello, World!";
console.log(message);
▶ 試してみよう

これは最もシンプルな例で、後のセクションで詳しく説明します。## 2. 機能要件

  1. Todo アイテムの追加 — 入力フィールド + ボタン。Enter キーまたはクリックで新しいアイテムを追加
  2. 完了/未完了のトグル — Todo をクリックして完了状態を切り替え(取り消し線効果)
  3. Todo アイテムの削除 — 各アイテムに削除ボタンが付いている
  4. フィルタリング — 3つのボタン:すべて / 未完了 / 完了済み
  5. ローカルストレージlocalStorage を使ってページ更新後もデータを永続化
  6. 残りアイテム数の表示 — 下部に「X 件残っています」と表示


2. 技術要件

コンCEPT 使用箇所
クラス TodoApp クラスですべてのロジックを整理
async/await localStorage の読み書きを非同期関数でラップ
DOM 操作 リスト項目の動的作成、コンテンツ更新、要素の削除
イベント処理 フォーム submit、リスト click、フィルタボタン click
JSON JSON.stringify で保存、JSON.parse で読み込み
配列メソッド filtermapfind で Todo データを管理
テンプレートリテラル HTML 文字列の構築


3. 受入基準



4. 参考実装アイデア

以下はヒントと方向性です。完全なコードではありません。自分で書くことで本当の学びが得られます。

(1) データ構造

HTML
<script>
// 各 Todo アイテムはオブジェクト
const todo = {
  id: Date.now(),      // ユニーク識別子
  text: 'JavaScript を学ぶ',
  completed: false
};
console.log(todo);

// すべての Todo アイテムは配列に格納
let todos = [];
</script>

(2) クラス構造

HTML
<script>
class TodoApp {
  constructor() {
    this.todos = [];         // データ
    this.filter = 'all';     // 現在のフィルタ
    this.init();             // 初期化
  }

  async init() {
    // 1. localStorage からデータを読み込み
    // 2. イベントをバインド
    // 3. リストをレンダリング
  }

  async save() {
    // JSON.stringify → localStorage.setItem
  }

  async load() {
    // localStorage.getItem → JSON.parse
  }

  addTodo(text) { /* オブジェクト作成、push、保存、レンダリング */ }
  toggleTodo(id) { /* アイテムを検索、completed を反転、保存、レンダリング */ }
  deleteTodo(id) { /* アイテムをフィルタアウト、保存、レンダリング */ }

  render() {
    // 1. this.filter に基づいて todos をフィルタ
    // 2. リストコンテナをクリア
    // 3. フィルタされた配列をループし、DOM ノードを作成
    // 4. カウント表示を更新
  }
}
</script>

(3) イベントバインド戦略

(4) レンダリング戦略

HTML
<script>
render() {
  const filtered = this.todos.filter(function(todo) {
    if (this.filter === 'active') return !todo.completed;
    if (this.filter === 'completed') return todo.completed;
    return true;  // 'all'
  }.bind(this));

  // コンテナをクリア
  this.listEl.innerHTML = '';

  filtered.forEach(function(todo) {
    var li = document.createElement('li');
    // コンテンツ、スタイル、データ属性を設定
    // トグルと削除イベントをバインド
    this.listEl.appendChild(li);
  }.bind(this));

  // カウントを更新
  var activeCount = this.todos.filter(function(t) { return !t.completed; }).length;
  this.countEl.textContent = activeCount + ' 件残っています';
}
</script>


5. 拡張アイデア

基本機能が動作したら、これらの高度なチャレンジに挑戦してみましょう。



6. 課題

上記の Todo アプリをすべての機能を実装し、すべての受入基準をクリアしてください。

推奨ステップ:

  1. HTML 構造と CSS スタイルから始める
  2. TodoApp クラスのスケルトンを構築(constructorinit
  3. addTodorender を実装。まず追加を動作させる
  4. toggleTodo を実装。完了/未完了の切り替え
  5. deleteTodo を実装。アイテムの削除
  6. フィルタリングを実装
  7. saveload を実装。データの永続化
  8. 残りアイテム数の表示
  9. 最終仕上げ:空入力のバリデーション、スタイルの調整
💡 ヒント: 一度にすべてを構築しようとしないでください。まず最小のものを動作させ、その後1つずつ追加していきましょう。ソフトウェア開発は「動かして、正しくして、速くする」です。


▶ サンプル

クラスと localStorage を使った完全な Todo アプリ:

HTML
<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: Arial, sans-serif; max-width: 600px; margin: 20px auto; }
    .done { text-decoration: line-through; color: gray; }
  </style>
</head>
<body>
  <h1>Todo アプリ</h1>
  <input id="taskInput" placeholder="新しいタスク">
  <button id="addBtn">追加</button>
  <ul id="taskList"></ul>
  <script>
    class Todo {
      constructor() { this.tasks = JSON.parse(localStorage.getItem('tasks') || '[]'); }
      add(text) { this.tasks.push({text, done: false}); localStorage.setItem('tasks', JSON.stringify(this.tasks)); this.render(); }
      toggle(i) { this.tasks[i].done = !this.tasks[i].done; localStorage.setItem('tasks', JSON.stringify(this.tasks)); this.render(); }
      render() { document.getElementById('taskList').innerHTML = this.tasks.map((t, i) => `<li class="${t.done ? 'done' : ''}">${t.text}</li>`).join(''); }
    }
    const todo = new Todo();
    document.getElementById('addBtn').onclick = () => todo.add(document.getElementById('taskInput').value);
    todo.render();
  </script>
</body>
</html>
▶ 試してみよう

クラスベースの状態管理、localStorage 永続化、動的 DOM レンダリングを示す完全な Todo アプリです。


❓ よくある質問

Q Classを使う理由は?
A Class構文はより明確で、constructorthis、継承が明確にサポートされています。内部はプロトタイプベースですが、オブジェクト指向言語のように書け、可読性が向上します。
Q localStorageのデータは失われますか?
A ユーザーが手動でブラウザデータを消去するかプライベートブラウジングを使用しない限り、localStorageのデータは永続的に保存されます。文字列しか保存できないため、オブジェクトは JSON.stringify が必要です。
Q イベントデリゲーションとは?Todoアプリでなぜ便利?
A 親要素にイベントリスナーをバインドし、e.target で子要素のイベントを処理する手法です。新しいタスク項目が自動的にイベント処理を得られ、再バインド不要でパフォーマンスも向上します。

📖 まとめ

📝 練習問題

  1. 基礎:コア Todo 機能を完成させてください。追加、完了トグル、削除を配列を使って実装(localStorage は不要)。
  2. 中級:基本版に localStorage による永続化とフィルタリング(すべて / 未完了 / 完了済み)を追加してください。ページ更新後もデータが保持されるようにしてください。
  3. チャレンジ:拡張機能を少なくとも1つ実装してください(ドラッグ&ドロップソート、編集、カテゴリタグ、テーマ切り替え、または期限リマインダー)。アプローチの簡単な説明も書いてください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%