【JavaScript / Local Storage】To-Doリストを実装してみた

先日、JavaScriptの「Local Storage」の基本的な使い方について記事をまとめました。

今回は、より実践的な使い方として「To Do リスト(ページを更新したり閉じたりしても内容が保存される)」を実装してみたので、その手順を公開します。

To-Do リスト

    • 入力フォームに To Do を入力して「Add」ボタンをクリックすると、To Do が追加される。複数追加も可能。
    • 追加された To Do には完了判定用のチェックボックスがあり、チェックを入れると取り消し線が付く。
    • 「Clear」ボタンをクリックするとアラートが表示され、「OK」をクリックするとTo Doがリセットされる。
    • 追加 / 完了チェックしたTo DoはLocal Storageに保存されるため、リロードしたりページを閉じて再度開いたりしても作業中の内容が表示され、リセットされない。(同じPC および ブラウザからのアクセスに限る。)
    目次

    HTML / CSS / JavaScript コード

    <p>To-Do List</p>
    <input type="text" id="new-todo" placeholder="New task...">
    <button id="add-btn">Add</button>
    <button id="clear-btn">Clear</button>
    <ul id="todo-list"></ul>
    p {
      font-size: 24px;
      font-weight: bold;
    }
    
    input, button {
      height: 24px;
      margin-right: 10px;
      font-size: 18px;
    }
    
    #todo-list {
      list-style-type: none;
      padding: 0;
    }
    
    #todo-list li {
      padding: 8px;
      border-bottom: 1px solid #aaa;
      display: flex;
      align-items: center;
    }
    
    #todo-list li.completed {
      text-decoration: line-through;
      color: grey;
    }
    
    #todo-list li input[type="checkbox"] {
      margin-right: 10px;
    }
    document.addEventListener('DOMContentLoaded', function() {
        const todoList = document.getElementById('todo-list'); // ToDoリストを表示するための要素
        const addBtn = document.getElementById('add-btn'); // ToDoを追加するためのボタン
        const clearBtn = document.getElementById('clear-btn'); // ToDoリストをクリアするためのボタン
        const newTodoInput = document.getElementById('new-todo'); // 新しいToDoを入力するための入力フィールド
    
        // Local StorageからToDoを読み込む
        let todos = JSON.parse(localStorage.getItem('todos')) || [];
    
        // ToDoを表示する関数
        function renderTodos() {
            todoList.innerHTML = ''; // ToDoリストをクリア
            todos.forEach((todo, index) => {
                const li = document.createElement('li'); // ToDoのリスト項目を作成
                const checkbox = document.createElement('input'); // 完了状態を示すチェックボックスを作成
                checkbox.type = 'checkbox';
                checkbox.checked = todo.completed; // ToDoの完了状態を設定
                checkbox.addEventListener('change', () => toggleComplete(index)); // チェックボックスの状態が変わったときの処理を追加
                li.appendChild(checkbox);
    
                const span = document.createElement('span'); // ToDoの内容を表示する要素を作成
                span.textContent = todo.text;
                if (todo.completed) {
                    li.classList.add('completed'); // 完了したToDoには線を引く
                }
                li.appendChild(span);
                todoList.appendChild(li); // ToDoリストにリスト項目を追加
            });
        }
    
        // 新しいToDoを追加する関数
        function addTodo() {
            const text = newTodoInput.value.trim(); // 入力フィールドからテキストを取得
            if (text !== '') {
                todos.push({ text, completed: false }); // 新しいToDoを配列に追加
                newTodoInput.value = ''; // 入力フィールドをクリア
                saveTodos(); // ToDoをLocal Storageに保存
                renderTodos(); // ToDoを再表示
                newTodoInput.focus(); // カーソルを入力フィールドに戻す
            }
        }
    
        // ToDoの完了状態を切り替える関数
        function toggleComplete(index) {
            todos[index].completed = !todos[index].completed; // ToDoの完了状態を反転
            saveTodos(); // ToDoをLocal Storageに保存
            renderTodos(); // ToDoを再表示
        }
    
        // 全てのToDoをクリアする関数
        function clearTodos() {
            if (confirm('To Do リストをすべてクリアします。よろしいですか?')) {
                todos = []; // ToDoの配列を空にする
                saveTodos(); // ToDoをLocal Storageに保存
                renderTodos(); // ToDoを再表示
                newTodoInput.focus(); // カーソルを入力フィールドに戻す
            } else {
                newTodoInput.focus(); // カーソルを入力フィールドに戻す
            }
        }
    
        // ToDoをLocal Storageに保存する関数
        function saveTodos() {
            localStorage.setItem('todos', JSON.stringify(todos)); // ToDoの配列を文字列に変換して保存
        }
    
        // イベントリスナーを設定
        addBtn.addEventListener('click', addTodo); // Addボタンをクリックしたときの処理
        clearBtn.addEventListener('click', clearTodos); // Clearボタンをクリックしたときの処理
        newTodoInput.addEventListener('keypress', function(event) {
            if (event.key === 'Enter') { // Enterキーを押したときの処理
                addTodo();
            }
        });
    
        // 初期表示
        renderTodos(); // ページ読み込み時にToDoを表示
        newTodoInput.focus(); // 初期表示時にカーソルを入力フィールドに設定
    });

    実装のポイント

    「Local Storage」に関する部分を中心に、JavaScriptのコードを解説します。

    Local Storage から保存されているTo-Doリストを取得

    let todos = JSON.parse(localStorage.getItem('todos')) || [];

    localStorage.getItemで保存されているTo-Doリスト(文字列)を取得し、JSON.parseを使ってJavaScriptのオブジェクトに変換します。保存されているTo-Doリストがない場合は空の配列[]を返します。

    タスクを表示する関数 (renderTodos)

    function renderTodos() {
        todoList.innerHTML = ''; // ToDoリストをクリア
        todos.forEach((todo, index) => {
            const li = document.createElement('li'); // ToDoのリスト項目を作成
            const checkbox = document.createElement('input'); // 完了状態を示すチェックボックスを作成
            checkbox.type = 'checkbox';
            checkbox.checked = todo.completed; // ToDoの完了状態を設定
            checkbox.addEventListener('change', () => toggleComplete(index)); // チェックボックスの状態が変わったときの処理を追加
            li.appendChild(checkbox);
    
            const span = document.createElement('span'); // ToDoの内容を表示する要素を作成
            span.textContent = todo.text;
            if (todo.completed) {
                li.classList.add('completed'); // 完了したToDoには線を引く
            }
            li.appendChild(span);
            todoList.appendChild(li); // ToDoリストにリスト項目を追加
        });
    }
    1. まず、To-Doリストをクリアします。
    2. 次に、Local Storage から取得したTo-Doリストを一つずつ取り出し、各To-Doをリスト項目の要素として作成します。各To-Doには以下2つの要素を含みます。
      • 完了ステータスを判定するcheckbox要素(チェックをいれるとテキストカラーがグレー&取り消し線が付く)
      • To-Doの内容を表示するspan要素

    新しいタスクを追加する関数 (addTodo)

    // 新しいToDoを追加する関数
    function addTodo() {
        const text = newTodoInput.value.trim(); // 入力フィールドからテキストを取得
        if (text !== '') {
            todos.push({ text, completed: false }); // 新しいToDoを配列に追加
            newTodoInput.value = ''; // 入力フィールドをクリア
            saveTodos(); // ToDoをLocal Storageに保存
            renderTodos(); // ToDoを再表示
            newTodoInput.focus(); // カーソルを入力フィールドに戻す
        }
    }
    1. 入力フィールドのテキストを取得し、新しいTo-Doを作成して配列todosに追加します。
      • 「取得したテキストに対して.trim()を使うと、文字列の両端にある空白を取り除くことができます。
    2. todosにはその内容textに加え、完了ステータスを判定するためのcompletedもセットします。
    3. To-Do追加後はTo-Doリストを保存saveTodos();し、To-Doリストを再表示renderTodos();します。
    4. 最後に、入力フィールドにカーソルを戻しますnewTodoInput.focus()。これによって次のTo-Doを作りやすくなり、ユーザビリティが向上します。

    To-Doの完了状態を切り替える関数 (toggleComplete)

    // ToDoの完了状態を切り替える関数
    function toggleComplete(index) {
        todos[index].completed = !todos[index].completed; // ToDoの完了状態を反転
        saveTodos(); // ToDoをLocal Storageに保存
        renderTodos(); // ToDoを再表示
    }

    indexに紐づいたTo-Doの完了状態(completed)を反転させたのち、To-Doリストを保存saveTodos();して再表示renderTodos();します。

    全てのタスクをクリアする関数 (clearTodos)

    // 全てのToDoをクリアする関数
    function clearTodos() {
        if (confirm('To Do リストをすべてクリアします。よろしいですか?')) {
            todos = []; // ToDoの配列を空にする
            saveTodos(); // ToDoをLocal Storageに保存
            renderTodos(); // ToDoを再表示
            newTodoInput.focus(); // カーソルを入力フィールドに戻す
        } else {
            newTodoInput.focus(); // カーソルを入力フィールドに戻す
        }
    }

    クリアボタンを押したときに確認ダイアログを表示し、ユーザーがOKを選択した場合にTo-Doをクリアtodos = [];します。その後の処理は他と同じで、To-Doリストを保存saveTodos();して再表示renderTodos();し、入力フィールドにカーソルを戻します。

    To-DoをLocal Storageに保存する関数 (saveTodos)

    // ToDoをLocal Storageに保存する関数
    function saveTodos() {
        localStorage.setItem('todos', JSON.stringify(todos)); // ToDoの配列を文字列に変換して保存
    }

    localStorage.setItem(key, value)を使い、’todos’というキーでTo-Doの配列todosをLocal Storageに保存します。

    ただし、todosは配列で各To-Doはオブジェクトとして保存されているため、JSON.stringifyを使ってJSON形式の文字列に変換します。(Local Storageに保存できるデータ型は文字列のみであるため)

    よかったらシェアしてね!
    • URLをコピーしました!
    • URLをコピーしました!
    目次