基礎から学ぶJavaScript入門 90日コース | ブラウザデータ保存 - Day 52:フィルター機能

JavaScript 90日で身につけるJavaScript
スポンサーリンク
スポンサーリンク

Day 52:フィルター機能

Day 52では、第4フェーズ「ブラウザデータ保存」の流れを引き継ぎながら、ToDoアプリにフィルター機能を追加します。今回作成するのは、「すべて」「未完了」「完了済み」の3種類の表示切り替えです。Day 51では検索機能を学び、入力されたキーワードをもとに filter() を使って条件に合うToDoだけを取り出す考え方を身につけました。Day 52ではその続きとして、今度は入力欄ではなく、状態に応じて一覧を切り替える仕組みを作ります。

検索機能とフィルター機能は似ていますが、少し役割が違います。検索機能は「文字列が含まれているか」という条件で絞り込みますが、今回のフィルター機能は「完了しているか、していないか」という状態で絞り込みます。つまり、Day 51では todo.text を見ていましたが、Day 52では todo.completed を見ます。この違いを理解すると、同じ filter() でも使い方がいろいろあることがわかります。

初心者の方にとって、今回の学習はとても大切です。なぜなら、ここで「配列のどの情報を見て、どう絞り込むか」を自分で考える力が育つからです。ToDoアプリに限らず、一覧表示を持つアプリでは、「すべて表示」「一部だけ表示」「条件ごとに切り替え」がよく出てきます。今回も、ただコードを写すのではなく、どんな考え方で組み立てるのかを、順を追って丁寧に説明していきます。

フィルター機能とは何か

フィルター機能とは、もともと持っている一覧データの中から、条件に合うものだけを表示する仕組みです。検索機能も広い意味ではフィルターの一種ですが、今回のフィルター機能は「状態による絞り込み」が中心です。

今回作る3つの表示は次の通りです。

  • すべて:すべてのToDoを表示する
  • 未完了:まだ終わっていないToDoだけを表示する
  • 完了済み:終わったToDoだけを表示する

初心者の方は、これを「同じToDo配列を、違う見せ方で表示する機能」と考えると理解しやすいです。データそのものを変えるのではなく、表示対象を切り替えているだけです。ここがとても大切です。

Day 52の学習で一番大事な考え方

今回の学習で一番大事なのは、「元データを消したり分けたりするのではなく、条件に応じて表示する配列を変える」という考え方です。たとえば未完了だけを見せたいからといって、完了済みToDoを削除してしまうわけではありません。元の todos 配列はそのまま持ち続け、表示するときだけ filter() を使って見せ方を変えます。

これは検索機能でも大切だった考え方ですが、Day 52ではさらに重要になります。なぜなら、「すべて」に戻したいからです。もしフィルターのたびに元データを書き換えてしまうと、あとで全件表示に戻せなくなります。初心者の方は、フィルター機能は「データ操作」ではなく「表示切り替え」だと意識すると理解しやすいです。

まずToDoデータの形を確認する

フィルター機能を作る前に、ToDoデータの形を改めて確認しておきます。これまでのToDoアプリでは、ToDo1件ごとに次のようなオブジェクトを使っていました。

const todo = {
  text: "JavaScriptを勉強する",
  completed: false
};
JavaScript

これが複数集まると配列になります。

const todos = [
  { text: "牛乳を買う", completed: false },
  { text: "JavaScriptを勉強する", completed: true },
  { text: "部屋を掃除する", completed: false }
];
JavaScript

今回のフィルター機能では、この completed の値を見て絞り込みます。

  • `completed: false` なら未完了です。
  • `completed: true` なら完了済みです。

初心者の方は、ここで「ToDoの表示文字だけでなく、完了状態もデータとして持っているからこそ、フィルターできる」と理解しておくことが大切です。

まずは小さな例で考える

いきなりアプリ全体を見る前に、小さな例で filter() の使い方を確認します。たとえば、完了済みだけを取り出すなら次のように書けます。

const todos = [
  { text: "牛乳を買う", completed: false },
  { text: "JavaScriptを勉強する", completed: true },
  { text: "部屋を掃除する", completed: false }
];

// 完了済みだけを取り出す
const completedTodos = todos.filter(function (todo) {
  return todo.completed === true;
});

console.log(completedTodos);
JavaScript

逆に、未完了だけを取り出すならこうです。

// 未完了だけを取り出す
const incompleteTodos = todos.filter(function (todo) {
  return todo.completed === false;
});

console.log(incompleteTodos);
JavaScript

この段階で、初心者の方は「filter() は文字列検索だけでなく、真偽値の条件でも使える」と理解できるとよいです。

もっとかみ砕いて考える

filter() の中でやっていることは、とてもシンプルです。ToDoを1件ずつ見て、

  • 完了済みフィルターなら、`completed` が `true` のものだけ残す
  • 未完了フィルターなら、`completed` が `false` のものだけ残す

という判断をしているだけです。

つまり、フィルター機能は「条件に合うかどうかの仕分け」です。初心者の方は、「難しい配列処理」と構えすぎず、「1件ずつ見て残すか捨てるかを決めている」と考えると理解しやすいです。

UIとして何が必要かを考える

フィルター機能をアプリとして使いやすくするには、ユーザーが表示モードを切り替えられる必要があります。そのため、今回は3つのボタンを用意するのがわかりやすいです。

  • すべて表示ボタン
  • 未完了表示ボタン
  • 完了済み表示ボタン

この形なら、初心者の方でも「どのボタンを押したら何が起きるか」が理解しやすいです。検索機能のように文字入力は必要なく、「見せたい種類を選ぶ」という発想になります。

最初に必要なHTMLを考える

まずは、ToDo一覧とフィルターボタンを含むHTMLを用意します。

<h2>ToDoフィルターアプリ</h2>

<input type="text" id="todoInput" placeholder="ToDoを入力してください">
<button id="addButton">追加</button>

<br><br>

<button id="allButton">すべて</button>
<button id="incompleteButton">未完了</button>
<button id="completedButton">完了済み</button>

<ul id="todoList"></ul>

このHTMLでは、ToDo追加欄に加えて、3つのフィルターボタンがあります。初心者の方は、まずUIとして「どの表示モードを選ぶ仕組みが必要か」を考えることが大切です。

表示関数を柔軟に作ることが重要です

検索機能と同じく、フィルター機能でも表示関数を柔軟にしておくことが大切です。つまり、常に todos 全体を表示するのではなく、「渡された配列を表示する」形にしておきます。

// 渡された ToDo 配列を画面に表示する関数
function renderTodos(list) {
  // いったん表示を空にする
  todoList.innerHTML = "";

  // 配列の内容を順番に表示する
  list.forEach(function (todo) {
    const li = document.createElement("li");
    li.textContent = todo.text;
    todoList.appendChild(li);
  });
}
JavaScript

こうしておけば、

  • `renderTodos(todos)` なら全件表示
  • `renderTodos(incompleteTodos)` なら未完了だけ表示
  • `renderTodos(completedTodos)` なら完了済みだけ表示

という使い分けができます。初心者の方は、「表示関数にどの配列を渡すかで見せ方を変えられる」という設計をしっかり理解するとよいです。

すべて表示の考え方

「すべて」は一番簡単です。単純に元の todos 配列をそのまま表示すればよいだけです。

// すべてボタンが押されたときの処理
allButton.addEventListener("click", function () {
  renderTodos(todos);
});
JavaScript

初心者の方は、「すべて」はフィルターしないモードだと考えるとわかりやすいです。つまり、元の配列をそのまま見せています。

未完了フィルターの作り方

未完了フィルターでは、completed が false のものだけを残します。

// 未完了ボタンが押されたときの処理
incompleteButton.addEventListener("click", function () {
  const incompleteTodos = todos.filter(function (todo) {
    return todo.completed === false;
  });

  renderTodos(incompleteTodos);
});
JavaScript

ここで大事なのは、元の todos 配列はそのままで、incompleteTodos という別の配列を作っていることです。初心者の方は、「未完了だけを取り出した結果を表示している」と理解するとよいです。

完了済みフィルターの作り方

完了済みフィルターでは、completed が true のものだけを残します。

// 完了済みボタンが押されたときの処理
completedButton.addEventListener("click", function () {
  const completedTodos = todos.filter(function (todo) {
    return todo.completed === true;
  });

  renderTodos(completedTodos);
});
JavaScript

これも未完了と考え方は同じです。違うのは条件だけです。初心者の方は、「条件の違いだけで別の一覧を作れる」というところに注目すると、filter() の便利さが見えてきます。

見た目に完了状態を反映する

ここは重要です。完了済みだけを表示しても、見た目で完了状態がわからなければ少し使いにくいです。そのため、表示するときに completed を見て見た目を変える処理も入れておくと実用的です。

// 渡された ToDo 配列を画面に表示する関数
function renderTodos(list) {
  todoList.innerHTML = "";

  list.forEach(function (todo) {
    const li = document.createElement("li");
    li.textContent = todo.text;

    // 完了済みなら取り消し線をつける
    if (todo.completed) {
      li.classList.add("completed");
    }

    todoList.appendChild(li);
  });
}
JavaScript

そしてCSSでクラスを定義します。

<style>
  .completed {
    text-decoration: line-through;
    color: gray;
  }
</style>

初心者の方は、フィルター機能は「どれを表示するか」だけでなく、「表示したものをどう見せるか」も大切だと意識すると理解が深まりやすいです。

まずはフィルター専用の小さな完成例

ここでは、すでにToDo配列がある前提で、フィルター表示だけを試せる小さな完成例を見てみます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Day 52 フィルター機能</title>
  <style>
    /* 完了済みの見た目 */
    .completed {
      text-decoration: line-through;
      color: gray;
    }
  </style>
</head>
<body>
  <h2>ToDoフィルターアプリ</h2>

  <button id="allButton">すべて</button>
  <button id="incompleteButton">未完了</button>
  <button id="completedButton">完了済み</button>

  <ul id="todoList"></ul>

  <script>
    // ボタンとリストを取得する
    const allButton = document.getElementById("allButton");
    const incompleteButton = document.getElementById("incompleteButton");
    const completedButton = document.getElementById("completedButton");
    const todoList = document.getElementById("todoList");

    // サンプル ToDo データ
    const todos = [
      { text: "牛乳を買う", completed: false },
      { text: "JavaScriptを勉強する", completed: true },
      { text: "部屋を掃除する", completed: false }
    ];

    // ToDo を表示する関数
    function renderTodos(list) {
      // 表示を空にする
      todoList.innerHTML = "";

      // 配列の内容を順番に表示する
      list.forEach(function (todo) {
        const li = document.createElement("li");
        li.textContent = todo.text;

        // 完了済みなら見た目を変える
        if (todo.completed) {
          li.classList.add("completed");
        }

        todoList.appendChild(li);
      });
    }

    // すべて表示
    allButton.addEventListener("click", function () {
      renderTodos(todos);
    });

    // 未完了だけ表示
    incompleteButton.addEventListener("click", function () {
      const incompleteTodos = todos.filter(function (todo) {
        return todo.completed === false;
      });

      renderTodos(incompleteTodos);
    });

    // 完了済みだけ表示
    completedButton.addEventListener("click", function () {
      const completedTodos = todos.filter(function (todo) {
        return todo.completed === true;
      });

      renderTodos(completedTodos);
    });

    // 最初は全件表示する
    renderTodos(todos);
  </script>
</body>
</html>

このコードは、Day 52の中心的な考え方を小さく体験するのにちょうどよいです。初心者の方は、まずこれを理解してから、ToDo追加機能や保存機能と組み合わせると学びやすいです。

ToDoアプリ Ver.2 に組み込む考え方

実際には、Day 50までで作ったToDoアプリ Ver.2 にこのフィルター機能を組み込んでいく形になります。そのときに大切なのは、「追加や完了切替でデータが変わったあと、今どのフィルターモードなのかも考える必要がある」という点です。

初心者向けの最初の段階では、追加や切替をしたあとは「すべて表示」に戻してしまってもかまいません。しかし、少し慣れてきたら「今未完了モードなら、その状態を保ったまま再表示する」ことも考えられます。つまり、フィルター機能は見た目だけの話に見えて、実は「現在の表示状態を管理する」考え方にもつながっています。

そのまま使える練習テンプレート

ここでは、ToDo追加とフィルター機能をまとめて試せる練習テンプレートを載せます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Day 52 フィルター機能</title>
  <style>
    /* 完了済み ToDo の見た目 */
    .completed {
      text-decoration: line-through;
      color: gray;
    }

    /* リスト項目の余白 */
    li {
      margin-bottom: 8px;
    }
  </style>
</head>
<body>
  <h2>ToDoフィルターアプリ</h2>

  <input type="text" id="todoInput" placeholder="ToDoを入力してください">
  <button id="addButton">追加</button>

  <br><br>

  <button id="allButton">すべて</button>
  <button id="incompleteButton">未完了</button>
  <button id="completedButton">完了済み</button>

  <ul id="todoList"></ul>

  <script>
    // 必要な要素を取得する
    const todoInput = document.getElementById("todoInput");
    const addButton = document.getElementById("addButton");
    const allButton = document.getElementById("allButton");
    const incompleteButton = document.getElementById("incompleteButton");
    const completedButton = document.getElementById("completedButton");
    const todoList = document.getElementById("todoList");

    // ToDo データ配列
    let todos = [];

    // ToDo を表示する関数
    function renderTodos(list) {
      // 表示を空にする
      todoList.innerHTML = "";

      // 配列の内容を1件ずつ表示する
      list.forEach(function (todo) {
        const li = document.createElement("li");
        li.textContent = todo.text;

        // 完了済みなら見た目を変える
        if (todo.completed) {
          li.classList.add("completed");
        }

        todoList.appendChild(li);
      });
    }

    // 追加ボタンの処理
    addButton.addEventListener("click", function () {
      const text = todoInput.value.trim();

      // 空欄なら追加しない
      if (text === "") {
        return;
      }

      // 新しい ToDo を追加する
      todos.push({
        text: text,
        completed: false
      });

      // 全件表示する
      renderTodos(todos);

      // 入力欄を空にする
      todoInput.value = "";
    });

    // すべて表示
    allButton.addEventListener("click", function () {
      renderTodos(todos);
    });

    // 未完了だけ表示
    incompleteButton.addEventListener("click", function () {
      const incompleteTodos = todos.filter(function (todo) {
        return todo.completed === false;
      });

      renderTodos(incompleteTodos);
    });

    // 完了済みだけ表示
    completedButton.addEventListener("click", function () {
      const completedTodos = todos.filter(function (todo) {
        return todo.completed === true;
      });

      renderTodos(completedTodos);
    });

    // 最初は空の状態で表示
    renderTodos(todos);
  </script>
</body>
</html>

このテンプレートは、フィルター表示の仕組みを学ぶための基本形です。ここにDay 50までの完了切替や保存機能を組み合わせることで、より実用的なアプリへ発展できます。

初心者が特に注意したいポイント

Day 52では、次の点を特に意識すると理解が深まりやすいです。

  • フィルター機能は元データを変更する機能ではありません。
  • `filter()` は条件に合う要素だけを新しい配列として取り出します。
  • 未完了か完了済みかの判断には `todo.completed` を使います。
  • 「すべて」はフィルターなしで元配列をそのまま表示します。
  • 表示関数を柔軟に作っておくと、いろいろな一覧切り替えに対応しやすくなります。

特に重要なのは、「表示の切り替え」と「データの削除や変更」を混同しないことです。初心者の方は、フィルターで見えなくなったToDoが消えたわけではない、としっかり理解しておく必要があります。

今後の発展につながる考え方

今回のフィルター機能を理解すると、さらにいろいろな応用が考えられます。

  • 件数ごとに「未完了が何件あるか」を表示する
  • 検索機能とフィルター機能を組み合わせる
  • 完了済みだけ一括削除する
  • 現在のフィルターモードを画面に表示する
  • フィルター状態も保存する

初心者の方は、まず今回の「すべて・未完了・完了済み」の3段階をしっかり理解してから、1つだけ発展機能を足してみるのがおすすめです。

情報セキュリティの観点での注意

今回のフィルター機能自体は主に画面表示の切り替えですが、ToDoデータの中身には引き続き注意が必要です。完了済み・未完了の分類が簡単にできるということは、保存されている情報が整理されているということでもあります。したがって、ブラウザ内に保存する内容は、機密性の高い個人情報や秘密情報ではなく、一般的なタスクやメモ程度にとどめるのが安全です。

初心者の方は、便利な表示機能を作るほど、「どんな情報を保存しているのか」もセットで意識するとよいです。

Day 52のゴールを整理する

Day 52では、次のことができるようになっていれば十分です。

  • フィルター機能の役割を説明できる
  • 「すべて」「未完了」「完了済み」の違いを説明できる
  • `filter()` を使って状態ごとにToDoを絞り込める
  • 表示関数に配列を渡して一覧を切り替えられる
  • 元データを残したまま見せ方だけ変える考え方を説明できる

ここまでできれば、Day 52としてはとても順調です。保存されたデータを扱うだけでなく、状態ごとに整理して見せる段階まで進めています。

まとめ

Day 52では、第4フェーズ「ブラウザデータ保存」の続きとして、ToDoアプリにフィルター機能を追加しました。今回作成したのは、「すべて」「未完了」「完了済み」の3つの表示切り替えであり、その中心となる考え方は filter() を使って条件に合うToDoだけを新しい配列として取り出し、それを表示することでした。未完了か完了済みかの判断には todo.completed を使い、元の todos 配列そのものは変更せず、表示対象だけを切り替えることが重要なポイントです。

特に大切なのは、フィルター機能はデータを消したり変更したりするものではなく、見せ方を変えるための仕組みだということです。この考え方が身につくと、検索、並べ替え、条件表示、カテゴリ分けなど、一覧データを扱う多くの機能が理解しやすくなります。

次に進む前に、今回のテンプレートを何も見ずにもう一度作り直してみるのがおすすめです。そして、そのあとで検索機能と組み合わせたり、完了件数や未完了件数を表示したりすると、配列操作と表示切り替えの理解がさらに深まりやすくなります。

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