HTML、CSS、JavaScriptToDoリストアプリWEBアプリ(JavaScript)

【JSコード解説】ToDoリストアプリ作成(5/5)

ToDoリストアプリのアイキャッチ画像05 HTML、CSS、JavaScript

WEBブラウザで動くToDoリストアプリのコード解説(taskManager.js、uiManager.js)

前回作成したWEBアプリのコード解説(taskManager.jsuiManager.js)です。

  • ToDoリストアプリ(1/5)
  • ToDoリストアプリ(2/5)
  • ToDoリストアプリ(3/5)
  • ToDoリストアプリ(4/5)
  • ToDoリストアプリ(5/5)
https://www.programming-make-and-learn.tech/todo-list-app-part5
  • WEBアプリのURL
  • GitHubのURL

GitHubのPagesで公開しているのでToDoリストアプリを使用できます。

ToDoリストアプリ

コード概要

  • TaskManagerクラスとUIManagerクラスの定義
  • タスクのCRUD操作(作成、読み取り、更新、削除)の実装
  • ローカルストレージを使用したタスクの永続化
  • タスクのUI表示インタラクションの管理
  • タスクの日付別グループ化ソート機能

taskManager.jsの解説

ここでは、TaskManager クラスの詳細な解説を行います。このクラスは、タスク管理システムの中核となる機能を提供します。

クラス定義とコンストラクタ

// タスク管理を行うクラス
export class TaskManager {
    // コンストラクタ
    constructor() {
        // タスクを読み込んで初期化
        this.tasks = this.loadTasks();
    }
}
  • export キーワードを使用してクラスを定義し、外部からインポート可能にしています。
  • コンストラクタ内で loadTasks() メソッドを呼び出し、タスクを初期化しています。

このコードは TaskManager クラスの基本構造を定義しています。export キーワードにより、このクラスは他のファイルからインポートして使用することができます。コンストラクタは、クラスのインスタンスが作成されたときに自動的に実行される特別なメソッドです。ここでは、loadTasks() メソッドを呼び出してタスクを読み込み、this.tasks に格納しています。

addTask メソッド

    // 新しいタスクを追加するメソッド
    addTask(taskText, date) {
        // 新しいタスクオブジェクトを作成
        const task = {
            id: Date.now(),  // 現在のタイムスタンプをIDとして使用
            text: taskText,  // タスクの内容
            completed: false,  // 完了状態(初期値は未完了)
            date: date  // タスクの日付
        };
        // タスクリストに新しいタスクを追加
        this.tasks.push(task);
        // タスクを保存
        this.saveTasks();
        // 作成したタスクを返す
        return task;
    }
  • 新しいタスクオブジェクトを作成し、ユニークな ID を割り当てています。
  • タスクを this.tasks 配列に追加し、saveTasks() メソッドで保存しています。
  • 作成したタスクオブジェクトを返しています。

このメソッドは新しいタスクを追加するために使用されます。タスクオブジェクトには、ユニークな ID(現在のタイムスタンプを使用)、タスクの内容、完了状態(初期値は false)、および日付が含まれます。タスクを配列に追加した後、saveTasks() メソッドを呼び出してローカルストレージに保存します。最後に、作成したタスクオブジェクトを返すことで、必要に応じて呼び出し元で使用できるようにしています。

deleteTask メソッド

    // タスクを削除するメソッド
    deleteTask(taskId) {
        // 指定されたIDのタスクを除外した新しい配列を作成
        this.tasks = this.tasks.filter(task => task.id !== taskId);
        // 変更を保存
        this.saveTasks();
    }
  • filter() メソッドを使用して、指定された ID のタスクを除外した新しい配列を作成しています。
  • 変更を saveTasks() メソッドで保存しています。

このメソッドは特定のタスクを削除するために使用されます。filter() メソッドは、条件に一致する要素だけを含む新しい配列を作成します。ここでは、指定された ID と一致しないタスクのみを残すことで、実質的にそのタスクを削除しています。その後、saveTasks() メソッドを呼び出して変更をローカルストレージに保存します。

toggleTaskStatus メソッド

    // タスクの完了状態を切り替えるメソッド
    toggleTaskStatus(taskId) {
        // 指定されたIDのタスクを見つける
        const task = this.tasks.find(task => task.id === taskId);
        if (task) {
            // タスクが見つかった場合、完了状態を反転
            task.completed = !task.completed;
            // 変更を保存
            this.saveTasks();
        }
    }
  • find() メソッドを使用して、指定された ID のタスクを見つけています。
  • タスクが見つかった場合、その完了状態を反転させています。
  • 変更を saveTasks() メソッドで保存しています。

このメソッドはタスクの完了状態を切り替えるために使用されます。まず、find() メソッドで指定された ID のタスクを探します。タスクが見つかった場合、その completed プロパティの値を反転させます(true なら false に、false なら true に)。最後に、saveTasks() メソッドを呼び出して変更を保存します。

editTask メソッド

    // タスクの内容を編集するメソッド
    editTask(taskId, newText) {
        // 指定されたIDのタスクを見つける
        const task = this.tasks.find(task => task.id === taskId);
        if (task) {
            // タスクが見つかった場合、内容を更新
            task.text = newText;
            // 変更を保存
            this.saveTasks();
        }
    }
  • find() メソッドを使用して、指定された ID のタスクを見つけています。
  • タスクが見つかった場合、その内容を新しいテキストで更新しています。
  • 変更を saveTasks() メソッドで保存しています。

このメソッドはタスクの内容を編集するために使用されます。指定された ID のタスクを見つけ、そのテキスト内容を新しい値で更新します。タスクが見つからない場合は何も行いません。変更があった場合は saveTasks() メソッドを呼び出して保存します。

saveTasks メソッド

    // タスクをローカルストレージに保存するメソッド
    saveTasks() {
        // タスクリストをJSON文字列に変換してローカルストレージに保存
        localStorage.setItem('tasks', JSON.stringify(this.tasks));
    }
  • JSON.stringify() を使用して、タスク配列を JSON 文字列に変換しています。
  • localStorage.setItem() を使用して、JSON 文字列をローカルストレージに保存しています。

このメソッドは現在のタスクリストをローカルストレージに保存するために使用されます。JSON.stringify() メソッドはJavaScriptオブジェクトをJSON文字列に変換します。これにより、複雑なデータ構造をテキストとして保存できます。localStorage.setItem() メソッドは、指定されたキー(ここでは ‘tasks’)に対応する値をローカルストレージに保存します。

loadTasks メソッド

    // ローカルストレージからタスクを読み込むメソッド
    loadTasks() {
        // ローカルストレージからタスクのJSON文字列を取得
        const tasksJSON = localStorage.getItem('tasks');
        // JSON文字列が存在する場合はパースして返す、存在しない場合は空配列を返す
        return tasksJSON ? JSON.parse(tasksJSON) : [];
    }
  • localStorage.getItem() を使用して、ローカルストレージからタスクの JSON 文字列を取得しています。
  • JSON.parse() を使用して、JSON 文字列をJavaScriptオブジェクトに変換しています。
  • タスクが存在しない場合は空の配列を返しています。

このメソッドはローカルストレージからタスクを読み込むために使用されます。まず、localStorage.getItem() メソッドで ‘tasks’ キーに対応する値(JSON文字列)を取得します。値が存在する場合は JSON.parse() メソッドでJavaScriptオブジェクトに変換します。値が存在しない場合(null の場合)は、空の配列を返します。これにより、アプリケーションを再起動しても以前のタスクを復元できます。

getTasks メソッド

    // 全タスクを取得するメソッド
    getTasks() {
        // タスクを日付順にソートして返す
        return this.tasks.sort((a, b) => new Date(a.date) - new Date(b.date));
    }
  • sort() メソッドを使用して、タスクを日付順にソートしています。
  • ソートされたタスク配列を返しています。

このメソッドは全タスクを取得するために使用されます。単にタスク配列を返すだけでなく、日付順にソートしてから返します。ソート関数では、各タスクの date プロパティを Date オブジェクトに変換し、その差を計算することで順序を決定しています。これにより、最も早い日付のタスクが配列の先頭に来るようになります。

uiManager.jsの解説

クラス定義と初期化

// UIを管理するクラス
export class UIManager {
    // コンストラクタ
    constructor(taskManager) {
        this.taskManager = taskManager; // TaskManagerのインスタンスを保持
        this.taskList = document.getElementById('task-list'); // タスクリストのDOM要素を取得
    }
}
  • UIManagerクラスを定義し、エクスポートしている
  • コンストラクタTaskManagerインスタンスとタスクリストのDOM要素を初期化している

このクラスはユーザーインターフェース(UI)を管理するためのものです。コンストラクタでは、タスク管理を行うTaskManagerのインスタンスを受け取り、内部で保持します。また、HTMLの’task-list’というIDを持つ要素を取得し、これをタスクリストとして使用します。

タスク追加メソッド

    // 新しいタスクを追加するメソッド
    addTask(taskText, date) {
        this.taskManager.addTask(taskText, date); // TaskManagerを使ってタスクを追加
        this.renderTasks(); // タスクリストを再描画
    }
  • addTaskメソッドは新しいタスクを追加する
  • TaskManagerを使ってタスクを追加し、UI更新のためにrenderTasksを呼び出す

このメソッドは、ユーザーが新しいタスクを入力したときに呼び出されます。TaskManagerのaddTaskメソッドを使ってタスクを追加し、その後renderTasksメソッドを呼び出してUIを更新します。

タスク要素作成メソッド

    // タスク要素を作成するメソッド
    createTaskElement(task) {
        const li = document.createElement('li'); // リスト項目要素を作成
        li.className = `task-item ${task.completed ? 'completed' : ''}`; // クラスを設定
        
        // タスク要素の内部HTMLを設定
        li.innerHTML = `
            <input type="checkbox" ${task.completed ? 'checked' : ''}>
            <span class="task-text" title="${task.text}">${task.text}</span>
            <div class="task-actions">
                <button class="edit-btn">編集</button>
                <button class="delete-btn">削除</button>
            </div>
        `;

        this.addTaskEventListeners(li, task); // イベントリスナーを追加

        return li; // 作成したタスク要素を返す
    }
  • 個々のタスクのHTML要素を作成する
  • タスクの完了状態に応じてクラスチェックボックスの状態を設定
  • タスクテキスト、編集ボタン、削除ボタンを含む
  • イベントリスナーを追加する

このメソッドは、1つのタスクを表示するためのHTML要素を作成します。リスト項目(<li>)を作成し、タスクの完了状態に応じてクラスを設定します。内部にチェックボックス、タスクテキスト、編集・削除ボタンを配置します。最後に、addTaskEventListenersメソッドを呼び出して、各要素にイベントリスナーを追加します。

イベントリスナー追加メソッド

    // タスク要素にイベントリスナーを追加するメソッド
    addTaskEventListeners(li, task) {
        const checkbox = li.querySelector('input[type="checkbox"]');
        const editBtn = li.querySelector('.edit-btn');
        const deleteBtn = li.querySelector('.delete-btn');

        // チェックボックスの状態変更イベント
        checkbox.addEventListener('change', () => this.toggleTaskStatus(task, li));
        // 編集ボタンのクリックイベント
        editBtn.addEventListener('click', () => this.editTask(task));
        // 削除ボタンのクリックイベント
        deleteBtn.addEventListener('click', () => this.deleteTask(task.id));
    }
  • タスク要素内の各部品(チェックボックス、編集ボタン、削除ボタン)にイベントリスナーを追加
  • それぞれのイベントに対応するメソッドを呼び出す

このメソッドは、タスク要素内の各部品にイベントリスナーを追加します。チェックボックスの変更、編集ボタンのクリック、削除ボタンのクリックそれぞれに対して、適切なメソッドを呼び出すように設定します。これにより、ユーザーの操作に応じてタスクを更新できるようになります。

タスク状態切り替えメソッド

    // タスクの完了状態を切り替えるメソッド
    toggleTaskStatus(task, li) {
        this.taskManager.toggleTaskStatus(task.id); // TaskManagerを使ってタスクの状態を切り替え
        li.classList.toggle('completed'); // タスク要素のクラスを切り替え
    }
  • TaskManagerを使ってタスクの完了状態を切り替える
  • タスク要素のクラスを切り替えて見た目を更新する

このメソッドは、チェックボックスがクリックされたときに呼び出されます。TaskManagerのtoggleTaskStatusメソッドを使ってタスクの完了状態を切り替え、同時にHTML要素のクラスを切り替えることで、完了状態の視覚的な表示を更新します。

タスク編集メソッド

    // タスクを編集するメソッド
    editTask(task) {
        const newText = prompt('タスクを編集:', task.text); // 新しいタスクテキストを入力
        if (newText && newText.trim() !== '') {
            this.taskManager.editTask(task.id, newText.trim()); // TaskManagerを使ってタスクを編集
            this.renderTasks(); // タスクリストを再描画
        }
    }
  • ユーザーに新しいタスクテキストをプロンプトで入力させる
  • 入力が有効な場合、TaskManagerを使ってタスクを更新し、UIを再描画する

このメソッドは、編集ボタンがクリックされたときに呼び出されます。ユーザーに新しいタスクテキストを入力させ、入力が空でない場合にTaskManagerのeditTaskメソッドを使ってタスクを更新します。その後、renderTasksメソッドを呼び出してUIを更新します。

タスク削除メソッド

    // タスクを削除するメソッド
    deleteTask(taskId) {
        this.taskManager.deleteTask(taskId); // TaskManagerを使ってタスクを削除
        this.renderTasks(); // タスクリストを再描画
    }
  • TaskManagerを使って指定されたIDのタスクを削除する
  • UIを再描画する

このメソッドは、削除ボタンがクリックされたときに呼び出されます。TaskManagerのdeleteTaskメソッドを使って指定されたIDのタスクを削除し、その後renderTasksメソッドを呼び出してUIを更新します。

タスクリスト描画メソッド

    // タスクリストを描画するメソッド
    renderTasks() {
        this.taskList.innerHTML = ''; // タスクリストをクリア
        const tasks = this.taskManager.getTasks(); // すべてのタスクを取得
        const groupedTasks = this.groupTasksByDate(tasks); // タスクを日付ごとにグループ化

        // 日付でソート
        const sortedDates = Object.keys(groupedTasks).sort((a, b) => new Date(a) - new Date(b));

        // 各日付のタスクを描画
        sortedDates.forEach(date => {
            const dateHeader = document.createElement('h2');
            dateHeader.textContent = this.formatDate(date); // 日付ヘッダーを作成
            this.taskList.appendChild(dateHeader);

            // その日のタスクを描画
            groupedTasks[date].forEach(task => {
                this.taskList.appendChild(this.createTaskElement(task));
            });
        });
    }
  • タスクリストをクリアし、TaskManagerから全タスクを取得
  • タスクを日付ごとにグループ化し、日付順にソート
  • 各日付のヘッダーとタスクをDOMに追加

このメソッドは、タスクリスト全体を再描画します。まず、既存のタスクリストをクリアし、TaskManagerから全タスクを取得します。次に、タスクを日付ごとにグループ化し、日付順にソートします。そして、各日付のヘッダーを作成し、その下にその日のタスクを追加していきます。これにより、タスクが日付ごとに整理された状態で表示されます。

タスクのグループ化メソッド

    // タスクを日付ごとにグループ化するメソッド
    groupTasksByDate(tasks) {
        return tasks.reduce((acc, task) => {
            const dateKey = this.formatDateKey(task.date);
            if (!acc[dateKey]) {
                acc[dateKey] = [];
            }
            acc[dateKey].push(task);
            return acc;
        }, {});
    }
  • タスクを日付ごとにグループ化する
  • reduceメソッドを使用して、日付をキーとするオブジェクトを作成

このメソッドは、タスクの配列を受け取り、日付ごとにグループ化したオブジェクトを返します。reduceメソッドを使用して、各タスクの日付をキーとし、そのキーに対応する配列にタスクを追加していきます。これにより、日付ごとにタスクをまとめることができます。

日付キーフォーマットメソッド

    // 日付キーをフォーマットするメソッド
    formatDateKey(dateString) {
        const date = new Date(dateString);
        return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
    }
  • 日付文字列をYYYY-MM-DD形式にフォーマットする
  • 月と日を2桁の文字列にパディングする

このメソッドは、日付文字列を受け取り、YYYY-MM-DD形式の文字列に変換します。月と日は常に2桁になるようにpadStartメソッドでゼロパディングします。これにより、日付の一貫したソートと比較が可能になります。

日付表示フォーマットメソッド

    // 日付を表示用にフォーマットするメソッド
    formatDate(dateString) {
        const date = new Date(dateString);
        const days = ['日', '月', '火', '水', '木', '金', '土'];
        return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}日(${days[date.getDay()]})`;
    }
  • 日付文字列を日本語形式にフォーマットする
  • 年月日と曜日を含む

このメソッドは、日付文字列を受け取り、「YYYY年MM月DD日(曜日)」形式の日本語表記に変換します。曜日は配列から取得し、日本語の曜日表記を追加します。これにより、ユーザーにとって読みやすい日付表示が可能になります。

まとめ

コードの特徴

  • モジュラー設計による責任の分離(TaskManagerとUIManager)
  • ES6クラス構文の使用
  • ローカルストレージAPIを活用したデータ永続化
  • イベント駆動型のユーザーインターフェース設計
  • 日付操作フォーマットのカスタム実装
  • 動的なDOM操作によるタスクリストの更新
  • メソッドチェーニングアロー関数の活用
  • 条件演算子テンプレートリテラルの使用

このJavaScriptコードは、効率的で保守性の高いToDoリストアプリケーションの核心部分を実装しています。コードはTaskManagerUIManagerという2つの主要クラスに分割されており、これにより関心の分離が実現され、コードの可読性と保守性が大幅に向上しています。

TaskManagerクラスは、タスクデータの管理に特化しており、タスクの追加、削除、編集、状態の切り替えなどの基本的なCRUD操作を担当しています。また、ローカルストレージを利用してタスクデータを永続化する機能も備えています。これにより、ブラウザを閉じても、ユーザーのタスクデータが保持されます。

一方、UIManagerクラスは、ユーザーインターフェースの管理と、TaskManagerとのインタラクションを担当しています。このクラスは、タスクの表示、ユーザー入力の処理、DOM操作などを行います。特筆すべき機能として、タスクを日付ごとにグループ化して表示する機能があり、これによりユーザーは日付別にタスクを整理して見ることができます。

コード全体を通して、モダンなJavaScript機能が効果的に活用されています。ES6のクラス構文、アロー関数、テンプレートリテラルなどの使用により、コードはより簡潔で読みやすくなっています。また、条件演算子やメソッドチェーニングなどのテクニックも適切に使用されており、効率的なコーディングスタイルが実践されています。

さらに、このコードはイベント駆動型の設計を採用しており、ユーザーのアクションに応じて適切にUIを更新します。これにより、リアルタイムで反応する、インタラクティブなユーザー体験が実現されています。

  • ToDoリストアプリ(1/5)
  • ToDoリストアプリ(2/5)
  • ToDoリストアプリ(3/5)
  • ToDoリストアプリ(4/5)
  • ToDoリストアプリ(5/5)
https://www.programming-make-and-learn.tech/todo-list-app-part5

コメント

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