基礎から学ぶJavaScript入門 90日コース | 非同期処理とAPI - Day 68:API × 検索 × フィルター

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

Day 68:API × 検索 × フィルター

Day 68では、第5フェーズ「非同期処理とAPI」の総合演習として、API取得、検索、フィルター、表示更新 を組み合わせたアプリ作りに取り組みます。ここまでの学習では、fetch() によるAPI取得、async / await、ローディング表示、エラー処理、検索アプリ、DOM操作による表示形式の切り替えなどを学んできました。Day 68は、それらを1つにまとめて、「取得したデータを検索し、条件で絞り込み、結果をその場で更新表示する」という、かなり実践的な流れを体験する回です。

初心者の方にとって、このDay 68はとても大切です。なぜなら、ここで初めて「データを取る」「探す」「絞る」「画面を変える」という流れがひと続きのアプリとしてつながるからです。これまではAPI取得、検索、フィルター、表示がそれぞれ個別の練習として登場してきました。しかし実際のWebアプリでは、それらは別々に存在するのではなく、ひとつの体験の中で一緒に動きます。たとえば、商品一覧ページでキーワード検索し、さらにカテゴリで絞り込み、その結果がすぐ画面に反映される、というのは非常によくあるパターンです。Day 68では、その基本形を初心者向けにやさしく作ります。

今回の大事なポイントは、単に機能を並べることではありません。どの順番で処理するのか、どのデータを元に検索やフィルターをかけるのか、画面をどう更新するのか、という考え方が非常に重要です。初心者の方は、コードをいきなり長く書こうとすると混乱しやすいため、まずは「どんな流れのアプリなのか」を日本語で整理し、それを少しずつコードにしていくのが大切です。今回も、ステップバイステップで、例題・コード・テンプレートを交えながら、丁寧に説明していきます。

まず最初に結論をつかむ

最初に今回の内容を一言でまとめると、Day 68では「APIから取得した一覧データに対して、検索キーワードとフィルター条件を組み合わせ、表示内容をその都度更新する仕組み」を作ります。

今回の基本の流れは次の通りです。

  • APIから一覧データを取得する
  • 取得データを一度保存しておく
  • 検索キーワードを受け取る
  • フィルター条件を受け取る
  • 元データから条件に合うものだけを絞り込む
  • 結果を画面に表示する
  • 条件が変わるたびに表示を更新する

初心者の方は、まず「毎回APIを取り直す」のではなく、「一度取得したデータを検索・フィルターに使う」という考え方をつかむことが大切です。

今回なぜ総合演習なのか

ここはDay 68の中心です。なぜ今回が総合演習なのかというと、これまで学んだ複数の知識が1つのアプリの中でつながるからです。

たとえば今回使う知識には、次のようなものがあります。

  • API取得のための `fetch()`
  • 非同期処理のための `async / await`
  • データ保持のための変数
  • 検索のための文字列比較
  • フィルターのための `filter()`
  • 画面更新のためのDOM操作
  • 0件時表示やエラー処理

初心者の方は、「新しいことを大量に覚える回」ではなく、「今まで学んだことを組み合わせる回」だと考えると取り組みやすいです。

今回作るアプリのイメージ

今回は、ユーザー一覧データを使った「検索+フィルター付き一覧表示アプリ」を作ることにします。たとえば、次のような動きを目指します。

  • APIからユーザー一覧を取得する
  • 名前で検索できる
  • 会社名の頭文字などで絞り込める
  • 条件に合うユーザーだけを一覧表示する
  • 検索文字やフィルター条件が変わるたびに表示が更新される

初心者の方は、まず「検索」と「フィルター」は少し役割が違うと理解すると整理しやすいです。

  • 検索:自由入力で探す
  • フィルター:決められた条件で絞る

検索とフィルターの違いを整理する

ここはとても大切です。検索とフィルターは似ていますが、考え方が少し違います。

  • 検索は、ユーザーが入力した文字に一致するものを探すことです。
  • フィルターは、カテゴリや条件に合うものだけ残すことです。

たとえば、

  • 検索キーワードが `Le` なら、名前に `Le` を含む人を探す
  • フィルターが `C` なら、会社名が `C` で始まる人だけに絞る

というようなイメージです。初心者の方は、検索を「文字で探す」、フィルターを「条件でふるいにかける」と考えるとわかりやすいです。

まずはデータを一度取得して保持する

Day 68でとても重要なのがここです。検索するたび、フィルターするたびに毎回APIを呼ぶのではなく、最初に一度データを取得し、そのデータを保存しておきます。

たとえば、次のようにします。

let allUsers = [];
JavaScript

そして取得後に入れます。

allUsers = await response.json();
JavaScript

こうしておくと、検索やフィルターは allUsers を対象に何度でも実行できます。初心者の方は、「元データを保管しておく」ことが表示更新の土台になると理解すると、とても大切な考え方が身につきます。

なぜ元データを残しておく必要があるのか

もし検索結果そのものにさらに検索をかけ続けると、だんだん元の一覧に戻しにくくなります。そのため、いつでも最初の一覧に戻れるように、元データを別に持っておく必要があります。

たとえば、

  • `allUsers` は元データ
  • `filteredUsers` は条件で絞った結果

という形です。初心者の方は、「元データ」と「表示用データ」を分けて考える習慣を持つと、アプリが壊れにくくなります。

ステップ1:HTMLの部品を用意する

まずは必要なHTMLを用意します。今回は次の部品が必要です。

  • 検索入力欄
  • フィルター用のセレクトボックス
  • メッセージ表示エリア
  • 結果表示エリア

基本のHTMLは次のようになります。

<h2>ユーザー検索&フィルター</h2>

<input type="text" id="keywordInput" placeholder="名前で検索">
<select id="companyFilter">
  <option value="">すべての会社</option>
  <option value="C">Cで始まる会社</option>
  <option value="R">Rで始まる会社</option>
</select>

<p id="message">データを読み込みます。</p>
<ul id="resultList"></ul>

初心者の方は、「入力欄で検索」「セレクトで条件指定」「結果欄で表示」という役割の分担を先に理解すると、そのあとのJavaScriptが読みやすくなります。

ステップ2:APIからデータを取得する

まずは一覧データを取得して allUsers に保存します。

let allUsers = [];

async function loadUsers() {
  const response = await fetch("https://jsonplaceholder.typicode.com/users");
  allUsers = await response.json();

  console.log(allUsers);
}
JavaScript

ここでの大切なポイントは、取得したデータをすぐ表示に使うだけでなく、変数へ保管していることです。初心者の方は、この保存の一手間が後でとても重要になると覚えておくとよいです。

ステップ3:表示用の関数を作る

次に、ユーザー配列を受け取って画面に表示する関数を作ります。ここは検索やフィルターの結果を使い回すために大切です。

function showUsers(users) {
  const resultList = document.getElementById("resultList");
  resultList.innerHTML = "";

  users.forEach(function (user) {
    const li = document.createElement("li");
    li.textContent = `${user.name} / ${user.email} / ${user.company.name}`;
    resultList.appendChild(li);
  });
}
JavaScript

初心者の方は、「取得する関数」と「表示する関数」を分けると、検索・フィルター後も同じ表示関数が使えると理解すると整理しやすいです。

ステップ4:検索だけを先に作る

いきなり検索とフィルターを同時に実装するのではなく、まず検索だけを作ります。たとえば、入力されたキーワードを名前に含むユーザーだけを残します。

function searchUsers(keyword) {
  return allUsers.filter(function (user) {
    return user.name.toLowerCase().includes(keyword.toLowerCase());
  });
}
JavaScript

ここで toLowerCase() を使っているのは、大文字小文字を気にせず検索できるようにするためです。初心者の方は、「まず1つの条件で絞る」ことから始めると理解しやすいです。

ステップ5:フィルターだけを先に作る

次に、フィルターだけを作ります。今回は会社名の頭文字で絞る例にします。

function filterUsersByCompany(users, letter) {
  if (letter === "") {
    return users;
  }

  return users.filter(function (user) {
    return user.company.name.startsWith(letter);
  });
}
JavaScript

この関数では、フィルター条件が空ならそのまま返し、条件があるなら一致するものだけ残します。初心者の方は、「フィルターは今ある配列をさらに条件で絞るもの」と考えるとわかりやすいです。

ステップ6:検索とフィルターを組み合わせる

ここがDay 68の中心です。検索とフィルターを別々に作ったら、それを組み合わせます。流れとしては、まず検索し、その結果に対してさらにフィルターをかけます。

function updateDisplay() {
  const keyword = document.getElementById("keywordInput").value.trim();
  const companyLetter = document.getElementById("companyFilter").value;

  // まず検索
  let result = allUsers.filter(function (user) {
    return user.name.toLowerCase().includes(keyword.toLowerCase());
  });

  // 次にフィルター
  if (companyLetter !== "") {
    result = result.filter(function (user) {
      return user.company.name.startsWith(companyLetter);
    });
  }

  showUsers(result);
}
JavaScript

このコードでは、「検索結果に対してさらにフィルター」をかけています。初心者の方は、条件を1つずつ積み重ねるイメージを持つと理解しやすいです。

表示更新とは何か

ここでいう表示更新とは、検索欄やフィルター条件が変わったときに、その都度結果を再計算して画面を描き直すことです。つまり、表示を一度作って終わりではなく、条件に応じて何度でも変わるようにすることです。

初心者の方は、表示更新を「条件が変わるたびに、表示をいったん作り直すこと」と考えるとわかりやすいです。

ステップ7:イベントを設定する

検索入力やフィルター変更のたびに表示更新したいので、イベントを設定します。

document.getElementById("keywordInput").addEventListener("input", function () {
  updateDisplay();
});

document.getElementById("companyFilter").addEventListener("change", function () {
  updateDisplay();
});
JavaScript

ここでは、入力欄は input イベント、セレクトボックスは change イベントを使っています。初心者の方は、「ボタンを押さなくても条件変更で即更新できる」と体験できると、かなりアプリらしさを感じやすいです。

0件時の表示も忘れない

検索とフィルターを組み合わせると、0件になる可能性が高くなります。そのため、何も表示されないだけでは不親切です。0件時の表示を入れます。

function showUsers(users) {
  const resultList = document.getElementById("resultList");
  const message = document.getElementById("message");

  resultList.innerHTML = "";

  if (users.length === 0) {
    message.textContent = "条件に合うユーザーが見つかりませんでした。";
    return;
  }

  message.textContent = `${users.length}件のユーザーが見つかりました。`;

  users.forEach(function (user) {
    const li = document.createElement("li");
    li.textContent = `${user.name} / ${user.email} / ${user.company.name}`;
    resultList.appendChild(li);
  });
}
JavaScript

初心者の方は、「0件はエラーではなく、正常な検索結果の1つ」だと理解しておくことが大切です。

ローディングとエラー処理も組み込む

今回は総合演習なので、データ取得時にはローディングとエラー処理も入れておくと完成度が高まります。

  • 取得前に「読み込み中です…」を出す
  • 取得成功後に一覧を初期表示する
  • 取得失敗時にエラーメッセージを出す

初心者の方は、「総合演習だからこそ、成功時だけでなく失敗時や待ち時間も設計する」と考えると実践的です。

そのまま使える完成テンプレート

ここでは、Day 68の内容をまとめた完成テンプレートを紹介します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Day 68 API × 検索 × フィルター</title>
  <style>
    /* エラーメッセージ用 */
    .error {
      color: red;
    }

    /* 結果リストの余白 */
    li {
      margin-bottom: 8px;
    }
  </style>
</head>
<body>
  <h2>ユーザー検索&フィルター</h2>

  <input type="text" id="keywordInput" placeholder="名前で検索">
  <select id="companyFilter">
    <option value="">すべての会社</option>
    <option value="C">Cで始まる会社</option>
    <option value="R">Rで始まる会社</option>
  </select>

  <p id="message">データを読み込みます。</p>
  <ul id="resultList"></ul>

  <script>
    // APIから取得した元データを保存する変数
    let allUsers = [];

    // 必要な要素を取得する
    const keywordInput = document.getElementById("keywordInput");
    const companyFilter = document.getElementById("companyFilter");
    const message = document.getElementById("message");
    const resultList = document.getElementById("resultList");

    // ユーザー一覧を画面に表示する関数
    function showUsers(users) {
      // 前回の表示を空にする
      resultList.innerHTML = "";

      // 0件なら専用メッセージを表示する
      if (users.length === 0) {
        message.textContent = "条件に合うユーザーが見つかりませんでした。";
        return;
      }

      // 件数メッセージを表示する
      message.textContent = `${users.length}件のユーザーが見つかりました。`;

      // 1件ずつ li として表示する
      users.forEach(function (user) {
        const li = document.createElement("li");
        li.textContent = `${user.name} / ${user.email} / ${user.company.name}`;
        resultList.appendChild(li);
      });
    }

    // 検索条件とフィルター条件に応じて表示を更新する関数
    function updateDisplay() {
      // 入力されたキーワードを取得する
      const keyword = keywordInput.value.trim().toLowerCase();

      // 選択されたフィルター条件を取得する
      const companyLetter = companyFilter.value;

      // まず検索キーワードで絞り込む
      let filteredUsers = allUsers.filter(function (user) {
        return user.name.toLowerCase().includes(keyword);
      });

      // 次に会社名の頭文字でフィルターする
      if (companyLetter !== "") {
        filteredUsers = filteredUsers.filter(function (user) {
          return user.company.name.startsWith(companyLetter);
        });
      }

      // 結果を画面に表示する
      showUsers(filteredUsers);
    }

    // APIからデータを取得する関数
    async function loadUsers() {
      try {
        // 読み込み中メッセージ
        message.textContent = "データを読み込み中です...";
        message.classList.remove("error");

        // APIへリクエストを送る
        const response = await fetch("https://jsonplaceholder.typicode.com/users");

        // HTTPエラーを確認する
        if (!response.ok) {
          throw new Error("ユーザーデータの取得に失敗しました。");
        }

        // JSONデータを読み取る
        allUsers = await response.json();

        // 初期表示を行う
        updateDisplay();
      } catch (error) {
        // エラー時の表示
        message.textContent = "データの取得に失敗しました。時間をおいて再試行してください。";
        message.classList.add("error");

        // 開発者向けに詳細を表示する
        console.log("エラー詳細:", error);
      }
    }

    // 入力欄が変わったら表示を更新する
    keywordInput.addEventListener("input", function () {
      updateDisplay();
    });

    // フィルター条件が変わったら表示を更新する
    companyFilter.addEventListener("change", function () {
      updateDisplay();
    });

    // 最初にデータを取得する
    loadUsers();
  </script>
</body>
</html>

この完成テンプレートの重要な見どころ

初心者の方は、完成コードの中で特に次の点に注目すると理解しやすいです。

  • `allUsers` に元データを保存していること
  • `showUsers()` が表示担当であること
  • `updateDisplay()` が検索とフィルターの両方を担当していること
  • 入力や選択変更のたびに `updateDisplay()` を呼んでいること
  • 0件時表示とエラー時表示が分かれていること

特に重要なのは、「表示更新のたびに元データから再計算している」という点です。これによって、条件変更に強い作りになっています。

特に深掘りしたい重要ポイント

今回最も重要なのは、「元データを残しておき、そこから毎回検索とフィルターをかけ直す」という考え方です。初心者の方は、つい前回の結果にさらに検索を重ねたくなることがありますが、それでは条件を戻したときに元の一覧へ戻しにくくなります。そのため、元データは別に保持し、表示用の結果だけを毎回作り直す設計がとても大切です。

また、検索とフィルターを同時に扱うときは、「検索してからフィルター」でも「フィルターしてから検索」でも、同じ条件のAND検索であれば結果はほぼ同じです。しかし、初心者向けには「自由入力の検索を先に行い、その結果をさらに条件で絞る」と考えると、流れとして理解しやすいことが多いです。

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

今回も外部APIから取得したデータを画面へ表示しています。そのため、表示する内容は外部由来であるという意識が大切です。学習段階では信頼できる公開APIを使っていますが、基本としては textContent を使って安全に表示する習慣を守るとよいです。また、検索やフィルターは便利ですが、どのデータをどこまでユーザーに見せるかは常に設計上の判断が必要です。初心者の方は、「表示できる情報」と「表示すべき情報」は必ずしも同じではないと意識すると、安全で堅実な開発感覚が身につきます。

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

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

  • 元データを保存しておくことが重要です。
  • 検索とフィルターは役割が少し違います。
  • 表示更新は、条件変更のたびに結果を作り直すことです。
  • 0件はエラーではなく正常な結果の1つです。
  • 取得処理、絞り込み処理、表示処理を分けると整理しやすいです。

特に重要なのは、「データ取得」と「表示更新」を分けて考えることです。ここが整理できると、動的な一覧表示アプリが一気に作りやすくなります。

Day 68のゴールを整理する

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

  • APIから一覧データを取得できる
  • 取得したデータを変数に保持できる
  • 検索キーワードでデータを絞り込める
  • フィルター条件でさらにデータを絞り込める
  • 条件に応じて表示を更新できる
  • 0件時やエラー時の表示も適切に行える

ここまでできれば、Day 68としてはとても順調です。API取得、検索、フィルター、DOM更新が1つのアプリとしてしっかりつながっています。

まとめ

Day 68では、第5フェーズ「非同期処理とAPI」の総合演習として、API取得、検索、フィルター、表示更新を組み合わせたアプリ作りに取り組みました。今回の大きな学びは、APIから取得した一覧データを一度変数に保存し、その元データをもとに検索キーワードとフィルター条件を組み合わせて、表示内容をその都度更新するという流れです。技術的には、fetch() と async / await による取得、filter() による絞り込み、イベントによる再計算、DOM操作による表示更新がつながっています。

特に大切なのは、元データと表示用データを分けて考えること、そして検索とフィルターを条件として積み重ねることです。これが理解できると、商品一覧、記事一覧、メンバー一覧など、多くの実践的なアプリへ応用しやすくなります。また、0件表示やエラー表示まで含めて設計することで、使う人にとってわかりやすいアプリに近づきます。

次に進む前に、今回のテンプレートを実際に動かし、名前ではなくメールアドレスでも検索できるようにしたり、会社名のフィルター条件を増やしたりするのがおすすめです。そして、「元データ」「検索条件」「フィルター条件」「表示更新」がそれぞれどんな役割を持っているかを自分の言葉で説明できるようになると、Day 68の理解がかなり深まっています。

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