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 配列そのものは変更せず、表示対象だけを切り替えることが重要なポイントです。
特に大切なのは、フィルター機能はデータを消したり変更したりするものではなく、見せ方を変えるための仕組みだということです。この考え方が身につくと、検索、並べ替え、条件表示、カテゴリ分けなど、一覧データを扱う多くの機能が理解しやすくなります。
次に進む前に、今回のテンプレートを何も見ずにもう一度作り直してみるのがおすすめです。そして、そのあとで検索機能と組み合わせたり、完了件数や未完了件数を表示したりすると、配列操作と表示切り替えの理解がさらに深まりやすくなります。

