5日目のゴールと今日やること
5日目のテーマは
「検索 × ソート × フィルタ機能を“保存できる・再利用できる・管理しやすい”形に進化させる」
ことです。
ここまでであなたは、
- リアルタイム検索
- 昇順 / 降順ソート
- 複合条件フィルタ
- タグフィルタ(AND / OR)
- ページネーション
- デバウンス
- ハイライト表示
といった、実務レベルの一覧アプリの基礎を作り上げました。
5日目では、さらに一歩進んで
- 検索条件の保存(localStorage)
- 条件プリセット(ワンクリックで条件セット)
- データの追加・削除
- ソート UI の改善(状態に応じてアイコン変更)
- applyFilters の最適化
など、「毎日使える一覧アプリ」 に仕上げるための機能を実装します。
検索条件を保存する(localStorage)
なぜ保存が必要なのか
ユーザーは毎回同じ条件で検索することが多いです。
- 「30歳以上」
- 「frontend タグ」
- 「名前順の昇順」
これらを毎回設定し直すのは面倒です。
そこで、検索条件(filters / sortState / pagination)を
localStorage に保存します。
保存するデータの構造を決める
保存するオブジェクト
const savedState = {
filters,
sortState,
pagination
};
JavaScript保存関数
function saveState() {
try {
const json = JSON.stringify(savedState);
localStorage.setItem("listAppState", json);
} catch (error) {
console.error("状態の保存に失敗しました", error);
}
}
JavaScript読み込み関数
function loadState() {
const saved = localStorage.getItem("listAppState");
if (!saved) return;
try {
const parsed = JSON.parse(saved);
Object.assign(filters, parsed.filters);
Object.assign(sortState, parsed.sortState);
Object.assign(pagination, parsed.pagination);
applyFilters();
} catch (error) {
console.error("状態の読み込みに失敗しました", error);
}
}
JavaScript深掘りポイント
- JSON.stringify / JSON.parse は localStorage の基本
- 保存するのは「UI の状態」
- 読み込んだら applyFilters を呼んで即反映
これで「前回の続きから使えるアプリ」になります。
条件プリセットを作る(ワンクリックで条件セット)
UI のイメージ
<button class="preset" data-keyword="a" data-min="20" data-max="30">20〜30歳 / 名前に a</button>
<button class="preset" data-tag="frontend">Frontend のみ</button>
<button class="preset" data-sort="age-asc">年齢昇順</button>
プリセットのロジック
const presetButtons = document.querySelectorAll(".preset");
presetButtons.forEach(btn => {
btn.addEventListener("click", () => {
const keyword = btn.dataset.keyword || "";
const minAge = btn.dataset.min ? Number(btn.dataset.min) : null;
const maxAge = btn.dataset.max ? Number(btn.dataset.max) : null;
const tag = btn.dataset.tag || null;
const sort = btn.dataset.sort || null;
if (keyword !== undefined) filters.keyword = keyword;
if (minAge !== null) filters.minAge = minAge;
if (maxAge !== null) filters.maxAge = maxAge;
if (tag) filters.tags = [tag];
if (sort) {
const [key, order] = sort.split("-");
sortState.key = key;
sortState.order = order;
}
applyFilters();
});
});
JavaScript深掘りポイント
- data 属性で条件を柔軟に設定
- ボタンを押すだけで複数条件を一気にセット
- applyFilters で即反映
これが「実務でよく使うプリセット機能」です。
データの追加・削除を実装する
なぜ必要なのか
一覧アプリは「見るだけ」で終わらず、
データを追加・削除できる と一気に実用的になります。
データ追加フォームを作る
UI のイメージ
<input id="newName" placeholder="名前" />
<input id="newAge" type="number" placeholder="年齢" />
<button id="addUser">追加</button>
追加ロジック
addUser.addEventListener("click", () => {
const name = newName.value.trim();
const age = Number(newAge.value);
if (!name || !Number.isFinite(age)) {
alert("正しい名前と年齢を入力してください");
return;
}
const newUser = {
id: Date.now(),
name,
age,
tags: []
};
users.push(newUser);
applyFilters();
});
JavaScript深掘りポイント
- id は Date.now() で簡易生成
- users.push で追加
- applyFilters で即反映
データ削除機能を追加する
renderList に削除ボタンを追加
function renderList(arr) {
if (!arr.length) {
listDiv.textContent = "該当するユーザーはいません。";
return;
}
let html = "";
arr.forEach(user => {
const name = highlight(user.name, filters.keyword);
html += `
<p>
${name}(${user.age}歳)
<button class="delete" data-id="${user.id}">削除</button>
</p>
`;
});
listDiv.innerHTML = html;
const deleteButtons = document.querySelectorAll(".delete");
deleteButtons.forEach(btn => {
btn.addEventListener("click", () => {
const id = Number(btn.dataset.id);
deleteUser(id);
});
});
}
JavaScript削除ロジック
function deleteUser(id) {
const index = users.findIndex(u => u.id === id);
if (index !== -1) {
users.splice(index, 1);
applyFilters();
}
}
JavaScript深掘りポイント
- findIndex で対象を探す
- splice で削除(破壊的だが users は「データベース」扱いなので OK)
- applyFilters で即反映
ソート UI を改善する(アイコン切り替え)
ボタンの表示を動的に変更
function updateSortButtons() {
sortAge.textContent =
sortState.key === "age"
? `年齢 ${sortState.order === "asc" ? "▲" : "▼"}`
: "年齢 ▲▼";
sortName.textContent =
sortState.key === "name"
? `名前 ${sortState.order === "asc" ? "▲" : "▼"}`
: "名前 ▲▼";
}
JavaScriptapplyFilters の最後に追加します。
updateSortButtons();
JavaScript深掘りポイント
- 現在のソート状態を UI に反映
- ユーザーが「今どうなっているか」を理解しやすくなる
applyFilters を最適化する
3日目までの applyFilters はこうでした
- キーワード
- 年齢
- タグ
- ソート
- ページネーション
これを「小さな関数の組み合わせ」にして読みやすくします。
applyFilters の完成形
function applyFilters() {
let result = [...users];
result = applyKeywordFilter(result);
result = applyAgeFilter(result);
result = applyTagFilter(result);
result = applySort(result);
result = applyPagination(result);
renderList(result);
updateSortButtons();
saveState();
}
JavaScript深掘りポイント
- 小さな関数の組み合わせで読みやすい
- UI 更新(renderList / updateSortButtons)
- 状態保存(saveState)
これが「中級者の一覧アプリの型」です。
5日目のまとめ
今日あなたがやったことを整理すると、こうなります。
- 検索条件を localStorage に保存
- 条件プリセットを実装
- データ追加・削除機能を追加
- ソート UI を改善(アイコン切り替え)
- applyFilters を小さな関数の組み合わせに最適化
- UI とロジックの分離をさらに強化
どれも実務で必須のテクニックです。
今日いちばん深く理解してほしいこと
5日目の本質は、
「一覧アプリは、状態(state)を保存し、再利用し、UI とロジックを分離することで“毎日使えるツール”になる」
ということです。
検索
→ フィルタ
→ ソート
→ ページネーション
→ 保存
→ 再利用
この流れを理解できたあなたは、
もう「ただ動くアプリ」ではなく
“使い続けられるアプリ” を作れる段階に来ています。
6日目では、この一覧アプリに
- データ編集(インライン編集)
- 並び替え(ドラッグ & ドロップ)
- UI の最適化
- 状態管理の高度化
などを加えて、さらに完成度を高めていきます。


