「リストを入力キーワードで絞り込みたい」「ボタンでカテゴリを切り替えたい」といったフィルター機能はjQueryで手軽に実装できます。本記事ではテキスト絞り込み・カテゴリボタン・複数条件AND絞り込み・テーブル対応・アニメーション・URL連動まで、実務で使えるパターンを網羅します。
- テキスト入力でリストをリアルタイム絞り込む基本実装
- 検索結果0件のときに「該当なし」メッセージを表示する方法
- データ属性(data-*)を使ったカテゴリボタン型フィルター
- テキスト検索+カテゴリの複数条件AND絞り込み
- テーブル行のフィルタリング(ヘッダー行を除外)
- fadeIn/fadeOutアニメーション付きフィルター
- フィルター状態をURLパラメーターに保存・復元する方法
- debounceによるパフォーマンス最適化
1. テキスト入力でリスト絞り込み(基本実装)
テキストボックスに文字を入力するたびに、マッチしない項目を非表示にする最もシンプルな実装です。
<input type="text" id="filter-input" placeholder="キーワードを入力..."> <ul id="item-list"> <li>JavaScript入門</li> <li>jQuery基礎</li> <li>CSSアニメーション</li> <li>HTML5フォーム</li> <li>jQueryプラグイン</li> </ul>
$(function () {
$('#filter-input').on('keyup', function () {
var keyword = $(this).val().toLowerCase(); // 小文字に統一
$('#item-list li').each(function () {
var text = $(this).text().toLowerCase();
var matched = text.includes(keyword); // キーワードを含むか
$(this).toggle(matched); // true=表示 / false=非表示
});
});
});
キーワードと比較対象テキストの両方を
toLowerCase() で小文字に変換することで、「jquery」と入力しても「jQuery」がヒットします。全角・半角の違いも吸収したい場合は String.prototype.normalize("NFKC") を組み合わせてください。2. 検索結果0件のときに「該当なし」を表示する
絞り込み結果が0件のとき、ユーザーに何も表示されないと不親切です。「該当なし」メッセージを出すことで離脱を防げます。
<input type="text" id="filter-input2" placeholder="キーワードを入力..."> <ul id="item-list2"> <li>JavaScript入門</li> <li>jQuery基礎</li> <li>CSSアニメーション</li> </ul> <p id="no-results" style="display:none; color:#999;">該当する項目がありません。</p>
$(function () {
$('#filter-input2').on('keyup', function () {
var keyword = $(this).val().toLowerCase();
var count = 0; // マッチ件数
$('#item-list2 li').each(function () {
var matched = $(this).text().toLowerCase().includes(keyword);
$(this).toggle(matched);
if (matched) count++;
});
// 0件のときだけ「該当なし」を表示
$('#no-results').toggle(count === 0 && keyword !== '');
});
});
3. カテゴリボタンで絞り込む(data属性活用)
ポートフォリオや製品一覧でよく見られる「ボタンを押してカテゴリ切り替え」のフィルターです。data-category 属性を使って絞り込み対象を指定します。
<div id="filter-buttons"> <button class="filter-btn is-active" data-filter="all">すべて</button> <button class="filter-btn" data-filter="js">JavaScript</button> <button class="filter-btn" data-filter="css">CSS</button> <button class="filter-btn" data-filter="html">HTML</button> </div> <ul id="cat-list"> <li data-category="js">jQueryの使い方</li> <li data-category="css">Flexboxレイアウト</li> <li data-category="js">非同期通信(Ajax)</li> <li data-category="html">フォームの作り方</li> <li data-category="css">CSSアニメーション</li> </ul>
$(function () {
$('.filter-btn').on('click', function () {
var filter = $(this).data('filter'); // 'all' / 'js' / 'css' / 'html'
// アクティブボタンの切り替え
$('.filter-btn').removeClass('is-active');
$(this).addClass('is-active');
// 絞り込み
if (filter === 'all') {
$('#cat-list li').show();
} else {
$('#cat-list li').each(function () {
var category = $(this).data('category');
$(this).toggle(category === filter);
});
}
});
});
クリックされたボタンに
is-active クラスを付与し、CSSで選択中のスタイルを当てます。classの追加・削除・切り替えで詳しく解説しています。4. テキスト+カテゴリの複数条件AND絞り込み
テキスト入力とカテゴリボタンを組み合わせ、両方の条件を満たす項目だけ表示するパターンです。ECサイトの絞り込み検索などで使えます。
HTML構造はセクション3と同様に data-category 属性を持つリストと、data-filter 属性を持つボタン群、加えてテキスト入力 id="search-word" を用意します。
$(function () {
var currentFilter = 'all'; // 現在のカテゴリフィルター
function applyFilter() {
var keyword = $('#search-word').val().toLowerCase();
$('#product-list li').each(function () {
var text = $(this).text().toLowerCase();
var category = $(this).data('category');
var textOk = text.includes(keyword);
var catOk = currentFilter === 'all' || category === currentFilter;
$(this).toggle(textOk && catOk); // AND条件
});
}
// テキスト入力時
$('#search-word').on('keyup', applyFilter);
// カテゴリボタンクリック時
$('.cat-btn').on('click', function () {
currentFilter = $(this).data('filter');
$('.cat-btn').removeClass('is-active');
$(this).addClass('is-active');
applyFilter();
});
});
複数のイベント(keyup・click)から同じ処理を呼び出すため、
applyFilter() という関数に切り出しています。フィルター条件が増えても applyFilter() 内だけ修正すればよいため保守性が高まります。5. テーブル行のフィルタリング(ヘッダー行を除外)
リストではなくテーブルを絞り込む場合、thead の行を対象外にすることがポイントです。
<input type="text" id="table-filter" placeholder="名前・部署で検索...">
<table id="member-table">
<thead><tr><th>名前</th><th>部署</th><th>役職</th></tr></thead>
<tbody>
<tr><td>山田 太郎</td><td>開発部</td><td>エンジニア</td></tr>
<tr><td>鈴木 花子</td><td>営業部</td><td>マネージャー</td></tr>
<tr><td>田中 一郎</td><td>開発部</td><td>リーダー</td></tr>
<tr><td>佐藤 由美</td><td>総務部</td><td>スタッフ</td></tr>
</tbody>
</table>
$(function () {
$('#table-filter').on('keyup', function () {
var keyword = $(this).val().toLowerCase();
// tbody の tr のみ対象(thead の行は除外される)
$('#member-table tbody tr').each(function () {
var rowText = $(this).text().toLowerCase();
$(this).toggle(rowText.includes(keyword));
});
});
});
#member-table tbody tr と指定することで、thead 内の行はセレクターに引っかからず、ヘッダーが消えることはありません。より複雑なセレクターパターンは全ての要素を取得する方法完全ガイドを参照してください。6. アニメーション付きフィルター(fadeIn/fadeOut)
単純な show/hide ではなく、フェードアニメーションで滑らかに切り替えることでUXが向上します。fadeIn() / fadeOut() を使います。
$(function () {
$('.fade-btn').on('click', function () {
var filter = $(this).data('filter');
$('.fade-btn').removeClass('is-active');
$(this).addClass('is-active');
$('.card-item').each(function () {
var category = $(this).data('category');
var matched = filter === 'all' || category === filter;
if (matched) {
$(this).stop(true).fadeIn(300); // 表示時はフェードイン
} else {
$(this).stop(true).fadeOut(200); // 非表示時はフェードアウト
}
});
});
});
ボタンを素早く連打すると、前のアニメーションが完了する前に次のアニメーションが始まり、表示状態が乱れることがあります。
stop(true) で前のアニメーションを即座にキャンセルしてから次を開始します。詳しくはアニメーション完全ガイドを参照してください。7. フィルター状態をURLパラメーターに保存・復元する
URLパラメーターにフィルター状態を反映することで、「このURLを共有すると同じ絞り込みで表示される」という体験を提供できます。
$(function () {
var params = new URLSearchParams(location.search);
var initFilter = params.get('cat') || 'all';
var initKeyword = params.get('q') || '';
// URLパラメーターから初期状態を復元
$('#url-search').val(initKeyword);
$('[data-filter="' + initFilter + '"]').addClass('is-active');
applyUrlFilter(initKeyword, initFilter);
function applyUrlFilter(keyword, filter) {
$('#url-list li').each(function () {
var text = $(this).text().toLowerCase();
var cat = $(this).data('category');
var textOk = text.includes(keyword.toLowerCase());
var catOk = filter === 'all' || cat === filter;
$(this).toggle(textOk && catOk);
});
}
function updateURL(keyword, filter) {
var p = new URLSearchParams();
if (keyword) p.set('q', keyword);
if (filter !== 'all') p.set('cat', filter);
var qs = p.toString() ? '?' + p.toString() : location.pathname;
history.replaceState(null, '', qs); // ページ遷移なしでURLを更新
}
var currentCat = initFilter;
$('#url-search').on('keyup', function () {
var kw = $(this).val();
applyUrlFilter(kw, currentCat);
updateURL(kw, currentCat);
});
$('.url-btn').on('click', function () {
currentCat = $(this).data('filter');
$('.url-btn').removeClass('is-active');
$(this).addClass('is-active');
var kw = $('#url-search').val();
applyUrlFilter(kw, currentCat);
updateURL(kw, currentCat);
});
});
history.replaceState() はページを遷移させずにアドレスバーのURLだけ変更します。pushState() と違い履歴に積まれないため、フィルター操作のたびにブラウザバックが増えることを防げます。8. debounceでパフォーマンスを改善する
キーを押すたびにフィルター処理が走ると、項目数が多い場合に動作が重くなります。debounce(一定時間入力がなければ実行)を使うことで処理回数を削減できます。
// debounce関数(ライブラリ不要)
function debounce(fn, delay) {
var timer;
return function () {
var ctx = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function() { fn.apply(ctx, args); }, delay);
};
}
$(function () {
var doFilter = debounce(function () {
var keyword = $('#debounce-input').val().toLowerCase();
$('#debounce-list li').each(function () {
$(this).toggle($(this).text().toLowerCase().includes(keyword));
});
}, 200); // 入力が止まって200ms後に実行
$('#debounce-input').on('keyup', doFilter);
});
- 項目数が100件未満: debounceなしの keyup で十分
- 項目数が100〜500件: 100〜200ms の debounce が効果的
- 項目数が500件以上: debounce+仮想スクロールの検討を推奨
フィルターパターンの選び方まとめ
| パターン | 条件 | おすすめの用途 |
|---|---|---|
| テキスト絞り込み(基本) | keyupイベント | シンプルなリスト・辞書検索 |
| カテゴリボタン | data属性一致 | ポートフォリオ・製品一覧 |
| 複数条件AND | テキスト+カテゴリ | ECサイト絞り込み・社員検索 |
| テーブル行フィルター | tbody tr対象 | 管理画面・データ一覧 |
| アニメーション付き | fadeIn/fadeOut | カード型ギャラリー・ポートフォリオ |
| URL連動 | URLSearchParams | 共有可能な絞り込みページ |
まとめ
jQueryのフィルター機能は toggle() や data() の組み合わせで幅広いパターンを実現できます。要件に応じて本記事のパターンを組み合わせてください。
関連記事: classの追加・削除・切り替えを完全解説 / アニメーション完全ガイド / サジェスト機能の実装方法
よくある質問(FAQ)
toLowerCase() で小文字に変換してから比較します(セクション1参照)。全角英字も対象に含めたい場合は normalize("NFKC") でさらに正規化すると効果的です。$("li").filter(fn) はセレクターで取得した集合から条件に合う要素を返すメソッドです。本記事の実装は each() でループして toggle() で表示切り替えしており、同じ結果を読みやすい形で書いています。小規模なら each()、集合操作が多い場合は filter() が向いています。:contains("keyword") は大文字・小文字を区別するため、ユーザー入力の絞り込みには適していません。text().toLowerCase().includes() の組み合わせを推奨します。show() するのが基本的なアプローチです。function applyFilter() {...} のように外出しにしておき、Ajax完了コールバックで applyFilter() を呼ぶ設計にしてください。
