ひとりでのアプリ開発 - fineの備忘録 -

ひとりでアプリ開発をするなかで起こったことや学んだことを書き溜めていきます

Web開発 - JavaScript:ToDoリストを作ってみる

初めに

 JavaScriptの練習として、ToDoリストアプリを作成します。HTMLとJavaScriptの間でどのようにデータを渡すのか、体験的に学ぶことができます。

ゴールの確認

 HTML、CSSJavaScriptを用いて、ToDoリストアプリを作成してみます。

 機能は次の通りです。

  • タスクとその期日を追加できる
  • 期日は
  • タスクは期日でソートされるようにする
  • Deleteボタンを押すと、当該タスクが削除される

 なお、本記事では CodePen を使用して作成します。

ファイル

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ToDo List</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <h1>ToDo List</h1>
    <input type="text" id="taskInput" placeholder="Enter task">
    <button onclick="addTask()">Add Task</button>

    <table id="taskTable">
        <thead>
            <tr>
                <th>Task</th>
                <th>Deadline</th>
                <th>Delete</th>
            </tr>
        </thead>
        <tbody id="taskList"></tbody>
    </table>

    <script src="app.js"></script>
</body>
</html>
CSS
body {
    font-family: Arial, sans-serif;
    margin: 20px;
}

h1 {
    text-align: center;
}

#taskInput {
    width: 70%;
    padding: 8px;
    margin-bottom: 10px;
}

button {
    padding: 8px 16px;
    background-color: #007bff;
    color: white;
    border: none;
    cursor: pointer;
}

button:hover {
    background-color: #0056b3;
}

table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 20px;
}

th, td {
    border: 1px solid #ddd;
    padding: 8px;
    text-align: left;
}

th {
    background-color: #f2f2f2;
}

td button {
    background-color: #dc3545;
    color: white;
    border: none;
    padding: 6px 10px;
    cursor: pointer;
}

td button:hover {
    background-color: #bd2130;
}
JavaScript
let tasks = [];

function addTask() {
    const taskInput = document.getElementById('taskInput');
    const taskText = taskInput.value.trim();
    const taskDeadline = prompt('Enter deadline (yyyy-mm-dd)'); // ユーザーに期限を入力してもらう

    if (taskText !== '' && isValidDate(taskDeadline)) {
        tasks.push({ text: taskText, deadline: taskDeadline });
        taskInput.value = '';
        displayTasks();
    } else {
        alert('Please enter a valid task and deadline (yyyy-mm-dd).');
    }
}

function isValidDate(dateString) {
    // yyyy-mm-ddの形式の日付かどうかを確認するためのシンプルなバリデーション
    const regex = /^\d{4}-\d{2}-\d{2}$/;
    return regex.test(dateString);
}

// 期限でタスクをソートするヘルパー関数
function sortByDeadline() {
    tasks.sort((a, b) => {
        // 期限をDateオブジェクトに変換して比較
        const deadlineA = new Date(a.deadline);
        const deadlineB = new Date(b.deadline);
        return deadlineA - deadlineB;
    });
}

function displayTasks() {
    const taskList = document.getElementById('taskList');
    taskList.innerHTML = '';

    sortByDeadline(); // タスクを期限でソート

    tasks.forEach((task, index) => {
        const row = document.createElement('tr');

        const taskCell = document.createElement('td');
        taskCell.textContent = task.text;

        const deadlineCell = document.createElement('td');
        deadlineCell.textContent = task.deadline;

        const deleteCell = document.createElement('td');
        const deleteButton = document.createElement('button');
        deleteButton.textContent = 'Delete';
        deleteButton.onclick = function() {
            deleteTask(index);
        };
        deleteCell.appendChild(deleteButton);

        row.appendChild(taskCell);
        row.appendChild(deadlineCell);
        row.appendChild(deleteCell);

        taskList.appendChild(row);
    });
}

function deleteTask(index) {
    tasks.splice(index, 1);
    displayTasks(); // タスクを削除した後に表示を更新する
}

displayTasks();

コードの解説

 本記事は、JavaScriptに焦点を当てた記事のため、HTMLとCSSの解説は割愛します。

変数 tasks
let tasks = [];

 タスクを保持するための配列として、tasks を定義しています。

 addTask() 関数の解説で分かりますが、タスクのオブジェクトは、text(タスクの内容)と deadline(期限)のプロパティを持ちます。

addTask() 関数
function addTask() {
    // タスクの内容と期限を取得
    const taskInput = document.getElementById('taskInput');
    const taskText = taskInput.value.trim();
    const taskDeadline = prompt('Enter deadline (yyyy-mm-dd)');

    // 入力が有効であればタスクを追加し、表示を更新する
    if (taskText !== '' && isValidDate(taskDeadline)) {
        tasks.push({ text: taskText, deadline: taskDeadline });
        taskInput.value = '';
        displayTasks();
    } else {
        alert('Please enter a valid task and deadline (yyyy-mm-dd).');
    }
}

 addTask() 関数は HTMLファイルを見ると、Add Task ボタンに割り当てられていることが分かります。このときに、id="taskInput" が指定されている入力欄に書かれているテキストを document.getElementById('taskInput'); で取得します。

<input type="text" id="taskInput" placeholder="Enter task">
<button onclick="addTask()">Add Task</button>
const taskText = taskInput.value.trim();

 taskInput.value.trim(); では、trim() メソッドを使用し、文字列の両端からホワイトスペースを取り除き、元の文字列を変更せずに新しい文字列を返します。

const taskDeadline = prompt('Enter deadline (yyyy-mm-dd)');

 prompt() は、ブラウザでダイアログボックスを表示し、ユーザーにテキスト入力を求めるJavaScriptの関数です。ユーザーに「Enter deadline (yyyy-mm-dd)」というメッセージが表示され、ユーザーに日付(yyyy-mm-ddの形式で)を入力してもらうよう促します。ユーザーが日付を入力してダイアログボックスの「OK」ボタンをクリックすると、その日付が入力された変数に格納されます。

// 入力が有効であればタスクを追加し、表示を更新する
    if (taskText !== '' && isValidDate(taskDeadline)) {
        tasks.push({ text: taskText, deadline: taskDeadline });
        taskInput.value = '';
        displayTasks();
    } else {
        alert('Please enter a valid task and deadline (yyyy-mm-dd).');
    }

 taskText が空白でなく、inValidDate() 関数により期日として入力された文字列が yyyy-mm-dd の形であると判断された場合、tasks配列にtextとdeadlineが挿入されます。また、taskInputを空白にすることでタスクの入力欄を空にします。displayTasks() は、tasks配列に変化があったときに呼び出し、そのことを画面に表示するための関数です。

 taskText !== '' && isValidDate(taskDeadline)がfalseである場合、alert() 関数を使い、注意喚起のためのダイアログを表示します。

isValidDate() 関数
function isValidDate(dateString) {
    const regex = /^\d{4}-\d{2}-\d{2}$/;
    return regex.test(dateString);
}
const regex = /^\d{4}-\d{2}-\d{2}$/;

 上記の regex正規表現を表しています。正規表現は、テキストのパターンを表現するための記述法です。

  • ^ は文字列の先頭を表す。
  • $ は文字列の末尾を表す。
  • \d{n} はn桁の数字を表す。
  • - はハイフンを表す。

 regex.test(dateString); により、引数である dataStringが 4桁の数字-2桁の数字-2数字 の形である場合は true、そうでない場合は false を返す関数になっています。

 正確に月や日付であることを精査できていないため、13月のような存在しない日付が入力できてしまう状態ではあります。

sortByDeadline() 関数
function sortByDeadline() {
    tasks.sort((a, b) => {
        const deadlineA = new Date(a.deadline);
        const deadlineB = new Date(b.deadline);
        return deadlineA - deadlineB;
    });
}

 tasks 配列内のタスクを期限でソートするための関数です。sort() 関数は、負の数が返ってきた場合はそのまま、正の数が返ってきた場合は入れ替えるという処理をします。今回は、deadlineA - deadlineB によりソートされており、昇順(期日の早いものが先)にソートされます。

 なお、(a, b) => {} の部分はアロー関数を呼ばれる形をしています。

displayTasks() 関数
function displayTasks() {
    const taskList = document.getElementById('taskList');
    taskList.innerHTML = '';

    sortByDeadline(); // タスクを期限でソート

    tasks.forEach((task, index) => {
        // 各タスクをテーブルの行に追加する処理
        const row = document.createElement('tr');

        // タスク名を表示するセルを作成し、内容を設定
        const taskCell = document.createElement('td');
        taskCell.textContent = task.text;

        // 期限を表示するセルを作成し、内容を設定
        const deadlineCell = document.createElement('td');
        deadlineCell.textContent = task.deadline;

        // 削除ボタンを持つセルを作成
        const deleteCell = document.createElement('td');
        const deleteButton = document.createElement('button');
        deleteButton.textContent = 'Delete';

        // 削除ボタンがクリックされたときに削除関数を呼び出すイベントを設定
        deleteButton.onclick = function() {
            deleteTask(index);
        };

        // 削除ボタンをセルに追加
        deleteCell.appendChild(deleteButton);

        // セルを行に追加
        row.appendChild(taskCell);
        row.appendChild(deadlineCell);
        row.appendChild(deleteCell);

        // 行をテーブルに追加
        taskList.appendChild(row);
    });
}

 ToDoリスト内のタスクをテーブル形式で表示するための関数です。

 HTML内に id="taskList" を持つ部分があります。

<tbody id="taskList"></tbody>

 この関数を呼び出すことで、部分のHTMLをクリアし、追加することでタスクを表示するテーブルを再描画できます。

deleteTask() 関数
function deleteTask(index) {
    tasks.splice(index, 1);
    displayTasks(); // タスクを削除した後に表示を更新する
}

 tasks.splice(index, 1) により、index で指定された位置から1つの要素を削除します。

 要素を削除したことを画面に反映させるために、displayTasks() 関数を呼び出します。

機能を追加するなら

 最低限の機能を持ったToDoリストアプリが完成しました。

 機能を追加するのであれば、以下のようなものが考えられます。

  • ローカルストレージやデータベースを使用して、ユーザーのタスクを保存・復元できるようにする。
  • タスクの優先順位やカテゴリーを設定できるようにする。
  • ドラッグ&ドロップ機能を実装し、タスクの並び替えやカテゴリ分けができるようにする。
  • タスクを検索したり、特定の条件でフィルタリングする機能を追加する。
  • 期限切れのタスク、特定のカテゴリーのタスクなどを一覧表示できるようにする。

 現状、ブラウザを閉じてしまえば、データが残らない状態のため、保存・復元機能は必須になるかと思います。