Day 51:検索機能
Day 51では、第4フェーズ「ブラウザデータ保存」の流れを引き継ぎながら、検索機能を学びます。今回のテーマは、入力値取得と filter() を使って、ToDo検索を実装することです。ここまでの学習では、ToDoアプリ Ver.2 を通して、データ保存、ページ再読み込み対応、完了状態保存までできるようになりました。つまり、ToDoデータを配列として持ち、それを localStorage に保存し、画面をデータから作る流れはすでに理解している段階です。Day 51では、その配列データの中から「条件に合うものだけを探して表示する」という、新しい見方を学びます。
検索機能は、初心者の方にとって非常に良い練習題材です。なぜなら、今までは「全部表示する」ことが中心でしたが、検索では「必要なものだけを取り出す」という考え方が必要になるからです。この考え方は、ToDoアプリだけでなく、商品検索、メモ検索、記事一覧、ユーザー一覧など、さまざまなアプリで共通して使われます。つまり、検索機能を学ぶことは、配列をただ保存・表示する段階から一歩進み、「データの中から条件に合うものを選ぶ」段階に進むことでもあります。
今回の学習では、特に filter() が中心になります。ただし、いきなり filter() の書き方だけを覚えても理解は浅くなりがちです。大切なのは、まず「検索とは何をしているのか」「入力値をどう使うのか」「なぜ filter() が便利なのか」を順番に整理することです。今回も、ステップバイステップで、初心者の方がつまずきやすい点を丁寧にほぐしながら、実践までつなげていきます。
検索機能とは何か
検索機能とは、たくさんあるデータの中から、条件に合うものだけを取り出して表示する仕組みです。たとえば、ToDoが10件あるときに、「買い物」という言葉を入力したら、「買い物」を含むToDoだけを表示する、という動きです。
初心者の方は、検索機能を次のように考えるとわかりやすいです。
- まず検索キーワードを入力する
- 配列の中を1件ずつ確認する
- 条件に合うものだけを残す
- 残ったものだけを画面に表示する
つまり、検索とは「配列の中身を全部見るけれど、表示するのは一部だけ」という処理です。この「全部見るけれど一部だけ残す」という考え方が、filter() の役割につながります。
Day 51で学ぶ中心テーマ
今回のDay 51では、特に次の2つが中心になります。
- 入力値取得
- `filter()`
入力値取得は、検索欄に入力された文字をJavaScriptで受け取るために必要です。そして filter() は、その入力値を使ってToDo配列の中から条件に合うものだけを選ぶために使います。
ここで重要なのは、この2つが単独では意味を持ちにくいということです。検索欄の文字を取るだけでは検索はできませんし、filter() を知っていても条件に使う入力値がなければ実践になりません。つまり、Day 51では「入力された条件を使って配列を絞り込む」という流れ全体を理解することが大切です。
まずは入力値取得を復習する
検索機能の最初の一歩は、検索欄に入力された文字を取得することです。これはこれまでの入力欄操作と基本は同じで、value を使います。
たとえば、HTMLに検索欄があるとします。
<input type="text" id="searchInput" placeholder="検索キーワードを入力してください">
この入力値を取得するには、次のように書きます。
// 検索欄を取得する
const searchInput = document.getElementById("searchInput");
// 入力されている文字を取得する
const keyword = searchInput.value;JavaScriptこの keyword に、検索欄へ入力された内容が入ります。初心者の方は、「入力欄の中身を取り出すには value」という基本をここで改めて確認しておくとよいです。
検索ではいつ入力値を取得するのか
ここで初心者の方が気をつけたいのは、「いつ入力値を取得するか」です。検索機能では、ページ読み込み時に一度だけ取得しても意味がありません。なぜなら、検索欄の中身はユーザーが入力するたびに変わるからです。
そのため、検索では次のようなタイミングが考えられます。
- 検索ボタンを押したときに取得する
- 入力されるたびに取得する
初心者向けには、まず検索ボタンを押したときに検索する形が理解しやすいです。そのあとで、入力されるたびに検索結果を更新する形へ発展させると段階的に学びやすくなります。
filter() とは何か
filter() は、配列の中から条件に合う要素だけを取り出して、新しい配列を作るメソッドです。ここがDay 51の中心です。
たとえば、数字の配列から偶数だけを取り出すなら、次のように書けます。
const numbers = [1, 2, 3, 4, 5, 6];
// 偶数だけを取り出す
const evenNumbers = numbers.filter(function (number) {
return number % 2 === 0;
});
console.log(evenNumbers);JavaScriptこの結果、evenNumbers には [2, 4, 6] が入ります。
初心者の方は、filter() を「ふるいにかけるもの」と考えるとわかりやすいです。全部のデータを一度通して、条件に合うものだけが残るイメージです。
filter() の動きをかみ砕いて理解する
filter() の中では、配列の要素が1つずつ取り出されて、条件チェックされます。そして、条件が true になったものだけが新しい配列に残ります。
さきほどの例でいえば、次のような流れです。
- 1 を見る → 偶数ではない → 捨てる
- 2 を見る → 偶数である → 残す
- 3 を見る → 偶数ではない → 捨てる
- 4 を見る → 偶数である → 残す
つまり、filter() は1件ずつ見て判断する処理です。初心者の方は、この1件ずつ確認するイメージを持つと、検索機能への応用も理解しやすくなります。
文字列検索に必要な includes() も知っておく
ToDo検索では、「そのToDoの文字に検索キーワードが含まれているか」を調べたいことが多いです。ここで役立つのが includes() です。
たとえば、次のように使います。
const text = "牛乳を買う";
// 「買う」が含まれているかを確認する
const result = text.includes("買う");
console.log(result); // trueJavaScriptつまり、includes() は「その文字列の中に指定した文字が入っているか」を調べるメソッドです。初心者の方は、ToDo検索では filter() と includes() を組み合わせると覚えるとわかりやすいです。
まずは簡単な文字列配列検索をしてみる
いきなりToDoオブジェクトの配列で考えると少し複雑なので、まずは単純な文字列の配列で検索を体験してみます。
const fruits = ["りんご", "みかん", "ぶどう", "もも"];
// 「み」を含むものだけ取り出す
const result = fruits.filter(function (fruit) {
return fruit.includes("み");
});
console.log(result);JavaScriptこの結果は ["みかん"] になります。
この例はとても大切です。なぜなら、ToDo検索も考え方はほぼ同じだからです。違うのは、配列の中身が文字列そのものではなく、オブジェクトになっていることです。
ToDo検索では何を調べるのか
ToDoアプリでは、配列の中身は次のようなオブジェクトでした。
const todos = [
{ text: "牛乳を買う", completed: false },
{ text: "JavaScriptを勉強する", completed: true },
{ text: "部屋を掃除する", completed: false }
];JavaScriptこの場合、検索したいのはオブジェクト全体ではなく、その中の text です。つまり、「todo.text に検索キーワードが含まれているか」を確認します。
const keyword = "買う";
const result = todos.filter(function (todo) {
return todo.text.includes(keyword);
});JavaScriptこれで、「買う」を含むToDoだけが取り出されます。初心者の方は、ここで「オブジェクト配列では、どのプロパティを検索対象にするのかをはっきり決める」ことが大切だと意識するとよいです。
検索機能の全体の流れを整理する
ToDo検索を作るときの流れを、日本語で整理すると次のようになります。
- 検索欄にキーワードを入力する
- 検索ボタンを押す
- 入力値を取得する
- ToDo配列に `filter()` を使う
- 条件に合うToDoだけを新しい配列として取り出す
- その配列だけを画面に表示する
この流れをそのままコードにしていけば、検索機能は作れます。初心者の方は、いきなりコードだけを見るのではなく、こうした日本語の手順と対応づけることが大切です。
最初に必要なHTMLを考える
検索機能付きToDoアプリを作るには、検索欄と検索ボタンを追加します。
<h2>ToDo検索アプリ</h2>
<input type="text" id="todoInput" placeholder="ToDoを入力してください">
<button id="addButton">追加</button>
<br><br>
<input type="text" id="searchInput" placeholder="検索キーワードを入力してください">
<button id="searchButton">検索</button>
<button id="showAllButton">すべて表示</button>
<ul id="todoList"></ul>
このHTMLでは、ToDo追加欄と検索欄を分けています。検索結果を解除して全件表示に戻すために、「すべて表示」ボタンも用意しています。この形だと、検索の流れが初心者にもかなりわかりやすいです。
まずは表示関数を柔軟にする
検索機能を作るときに大切なのは、表示関数が「どの配列を表示するか」を柔軟に受け取れるようにすることです。普段は全ToDo配列を表示し、検索時には絞り込んだ配列を表示したいからです。
たとえば次のようにします。
// 指定された 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(filteredTodos) と書けば検索結果表示ができます。初心者の方は、「表示する配列を切り替えられるようにしておく」という設計がとても大切です。
検索ボタンの処理を作る
次に、検索ボタンを押したときの処理を書きます。
// 検索ボタンが押されたときの処理
searchButton.addEventListener("click", function () {
// 検索キーワードを取得する
const keyword = searchInput.value.trim();
// filter() で条件に合う ToDo を取り出す
const filteredTodos = todos.filter(function (todo) {
return todo.text.includes(keyword);
});
// 検索結果を表示する
renderTodos(filteredTodos);
});JavaScriptこのコードで、入力されたキーワードを含むToDoだけが表示されます。初心者の方は、ここで「検索とは、元の配列を壊すことではなく、絞り込んだ別の配列を作って表示すること」だと理解するとよいです。
空欄検索をどう扱うか
ここは重要なので少し深掘りします。検索欄が空のまま検索ボタンを押した場合、どうするべきでしょうか。実は includes("") は多くの場合 true になるため、空欄検索をすると全件表示に近い動きになります。しかし、学習段階では空欄なら全件表示に戻す、と明示的に書いたほうがわかりやすいです。
// 検索キーワードを取得する
const keyword = searchInput.value.trim();
// 空欄なら全件表示に戻す
if (keyword === "") {
renderTodos(todos);
return;
}JavaScript初心者の方は、「空欄のときどうするか」も機能設計の一部だと意識すると、実用的なアプリに近づきやすいです。
完成版テンプレート
ここでは、ToDo追加と検索を両方体験できる完成版テンプレートを載せます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Day 51 ToDo検索</title>
</head>
<body>
<h2>ToDo検索アプリ</h2>
<input type="text" id="todoInput" placeholder="ToDoを入力してください">
<button id="addButton">追加</button>
<br><br>
<input type="text" id="searchInput" placeholder="検索キーワードを入力してください">
<button id="searchButton">検索</button>
<button id="showAllButton">すべて表示</button>
<ul id="todoList"></ul>
<script>
// 必要な要素を取得する
const todoInput = document.getElementById("todoInput");
const addButton = document.getElementById("addButton");
const searchInput = document.getElementById("searchInput");
const searchButton = document.getElementById("searchButton");
const showAllButton = document.getElementById("showAllButton");
const todoList = document.getElementById("todoList");
// ToDo データを入れる配列
let todos = [];
// ToDo を画面に表示する関数
function renderTodos(list) {
// いったん表示を空にする
todoList.innerHTML = "";
// 渡された配列を順番に表示する
list.forEach(function (todo) {
const li = document.createElement("li");
li.textContent = todo.text;
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 = "";
});
// 検索ボタンの処理
searchButton.addEventListener("click", function () {
// 検索キーワードを取得する
const keyword = searchInput.value.trim();
// 空欄なら全件表示に戻す
if (keyword === "") {
renderTodos(todos);
return;
}
// 条件に合う ToDo を取り出す
const filteredTodos = todos.filter(function (todo) {
return todo.text.includes(keyword);
});
// 検索結果を表示する
renderTodos(filteredTodos);
});
// すべて表示ボタンの処理
showAllButton.addEventListener("click", function () {
renderTodos(todos);
});
</script>
</body>
</html>
このテンプレートでは、ToDoを追加し、そのあとキーワード検索ができます。初心者の方は、まずこれをしっかり動かし、「入力値取得」「filter()」「表示切り替え」の3つがどうつながっているかを確認すると理解しやすいです。
さらに一歩進んだ考え方
検索機能を理解するときに、もう1つ大事な考え方があります。それは、「検索結果は元データそのものではない」ということです。検索結果は、元の todos 配列から条件に合うものだけを選んだ別の配列です。つまり、filter() は元の配列を壊しません。
この性質は非常に重要です。なぜなら、検索を解除すれば元の一覧に戻したいからです。もし検索のたびに元データを書き換えてしまうと、あとで全件表示に戻せなくなります。初心者の方は、「検索は元データを変更せず、見せ方だけを変える」と理解すると、かなり設計が安定します。
初心者が特に注意したいポイント
Day 51では、次の点を特に意識すると理解が深まりやすいです。
- 検索欄の入力値は `value` で取得します。
- `filter()` は条件に合う要素だけを取り出して新しい配列を作ります。
- `includes()` を使うと、文字列にキーワードが含まれているかを調べられます。
- 検索結果は元の配列ではなく、絞り込まれた別の配列です。
- 空欄検索をどう扱うかをあらかじめ決めておくと使いやすいです。
特に重要なのは、「元データはそのままにして、表示する内容だけを変える」という考え方です。ここが理解できると、検索機能だけでなく並べ替えや絞り込み機能も考えやすくなります。
今後の発展につながるポイント
Day 51の検索機能は基本形ですが、ここから次のような発展も考えられます。
- 入力されるたびにリアルタイム検索する
- 大文字小文字の違いを無視して検索する
- 完了済みだけを検索対象にする
- 複数条件で絞り込む
- 件数も一緒に表示する
初心者の方は、まず今回の基本形をしっかり理解したうえで、1つだけ発展機能を足してみると学習が定着しやすいです。
情報セキュリティの観点での注意
今回の検索機能そのものに大きな危険があるわけではありませんが、検索対象にどんなデータを持たせるかは考える必要があります。localStorage に保存するToDoやメモなどは、基本的に機密性の高い情報ではないものにとどめるのが安全です。検索機能を作ると、保存されている内容を簡単に一覧・抽出できるようになるため、なおさら機密情報を扱わない設計が重要です。
初心者の方は、便利な機能を作るほど「どんな情報を持たせるか」も意識するようにすると、安全な設計の感覚が育ちます。
Day 51のゴールを整理する
Day 51では、次のことができるようになっていれば十分です。
- 入力欄から検索キーワードを取得できる
- `filter()` の役割を説明できる
- `includes()` を使って文字列検索ができる
- ToDo配列から条件に合うデータだけを取り出せる
- 検索結果だけを画面に表示できる
- 元データと検索結果が別物だと説明できる
ここまでできれば、Day 51としてはとても順調です。データを保存して表示する段階から一歩進み、必要なデータだけを取り出して見せる段階へ進めています。
まとめ
Day 51では、第4フェーズ「ブラウザデータ保存」の続きとして、検索機能を学びました。今回の中心は、入力値取得と filter() であり、検索欄に入力されたキーワードをもとに、ToDo配列の中から条件に合うものだけを取り出して表示する流れを理解することが目標でした。filter() は、配列の中身を1件ずつ確認し、条件に合う要素だけを残して新しい配列を作るメソッドです。そしてToDo検索では、todo.text.includes(keyword) という形で、各ToDoの文字に検索語が含まれているかを調べるのが基本になります。
特に重要なのは、検索は元のデータを壊す処理ではない、ということです。元の todos 配列はそのままにして、条件に合うものだけを集めた別の配列を作り、それを表示しています。この考え方は、検索だけでなく、並べ替えや条件絞り込み、分類表示などにも広くつながっていきます。
次に進む前に、今回のテンプレートを何も見ずにもう一度作り直してみるのがおすすめです。そのうえで、検索ボタンではなく入力されるたびに検索する形へ改造してみると、イベント処理と検索機能の理解がさらに深まりやすくなります。

