無料相談

迷ったら

どれを頼めばいいか
分からないときは

困りごとを選ぶと、担当のサービスが分かります。

困りごとから探す → 無料で相談する
サイトの機能・技術

JavaScriptの絞り込みフィルタで選択条件をURLに反映させる方法(共有・戻るボタン対応)

JavaScriptで作った絞り込みフィルタの選択条件を、URLに反映させる方法をサンプルコード付きで解説します。 URLSearchParamsとhistory.pushStateで、絞り込んだ状態の共有、戻るボタン、再訪問時の復元に対応できます。

インセンブル編集部 公開

商品一覧や事例一覧に絞り込みフィルタを設置するとき、選んだ条件をURLに反映させておくと、使い勝手が大きく上がります。この記事では、そのメリットと実装方法を、サンプルコード付きで紹介します。

使うのは、ブラウザーに標準で備わっている URLSearchParams と history.pushState の2つだけです。追加のライブラリは要りません。

絞り込み条件をURLに反映させると何が良いのか

たとえば「Web制作の事例だけを絞り込んだ状態」が example.com/cases?category=web のようなURLになると、次の3つのメリットが生まれます。

  • 01

    絞り込んだ状態のまま共有できる

    URLをそのまま送れば、相手にも同じ条件の一覧が表示されます。「この条件の結果を見てほしい」というやりとりや、営業資料・SNSでの紹介がスムーズになります。

  • 02

    戻る・進むボタンが正しく動く

    条件を変えるたびに履歴が残るので、ブラウザーの戻るボタンで前の条件に戻れます。URLに反映していないと、戻るボタンで一覧から出てしまい、絞り込みをやり直すことになります。

  • 03

    次に開いたときも同じ条件で表示できる

    ブックマークしておけば、次に開いたときも同じ条件で一覧が表示されます。詳細ページを見てから一覧に戻ったときも、条件が残ります。

基本の実装

考え方はシンプルで、やることは次の2つだけです。

  1. 条件が変わったら、URLに書き込む
  2. ページを開いたとき(と、戻る・進むボタンが押されたとき)は、URLから条件を読み取って絞り込みを再現する

ポイントは、絞り込みの状態を持っているのはURLだけにすることです。画面の表示は、いつもURLを読んで作り直します。こうしておくと、どこから開いても同じ結果になります。

HTML
<label for="category">カテゴリ</label>
<select id="category">
  <option value="">すべて</option>
  <option value="web">Web制作</option>
  <option value="print">印刷物</option>
</select>

<ul class="case-list">
  <li data-category="web">事例A</li>
  <li data-category="print">事例B</li>
  <li data-category="web">事例C</li>
</ul>

<script src="filter.js" defer></script>
JavaScript(filter.js)
const select = document.getElementById('category');
const items = document.querySelectorAll('.case-list li');

// URLから条件を読み取って、表示を合わせる
function applyFilter() {
  const params = new URLSearchParams(location.search);
  const category = params.get('category') || '';

  items.forEach((item) => {
    item.hidden = category !== '' && item.dataset.category !== category;
  });

  // セレクトボックスの表示もURLに合わせる
  select.value = category;
}

// 条件が変わったら、URLに書き込む(履歴にも追加する)
select.addEventListener('change', () => {
  const params = new URLSearchParams(location.search);

  if (select.value) {
    params.set('category', select.value);
  } else {
    params.delete('category');
  }

  const query = params.toString();
  history.pushState(null, '', query ? `?${query}` : location.pathname);
  applyFilter();
});

// 戻る・進むボタンに対応
window.addEventListener('popstate', applyFilter);

// ページを開いたときに、URLの条件で絞り込む
applyFilter();

コードのポイント解説

URLSearchParamsで、URLの条件を読み書きする

new URLSearchParams(location.search) で、今のURLの ? から後ろを操作できるようになります。get で読み取り、set で書き込み、delete で消します。文字列を自分で組み立てたり分解したりしなくてよいので、日本語などの値も自動で変換してくれます。

history.pushStateで、読み込み直さずにURLを変える

history.pushState を使うと、ページを読み込み直さずにURLだけを書き換え、履歴に積むことができます。location.search に直接代入してもURLは変わりますが、そのたびにページを読み込み直すので、画面が一瞬白くなり、操作が重く感じられます。

popstateイベントを必ず受け取る

戻る・進むボタンが押されると、URLは前の状態に戻り、popstate というイベントが発生します。これを受け取って applyFilter() を呼ばないと、URLは戻っているのに、表示は前の条件のままという食い違いが起きます。

「すべて」のときはパラメータを消す

「すべて」を選んだら params.delete('category') で条件を消し、パラメータが何も残らないときは、? を付けずにページのパスだけに戻します。?category= のような空の条件が残ると、共有されたときに分かりにくくなります。

表示の切り替えは hidden 属性で

当てはまらない項目は hidden 属性で隠しています。見た目はCSS、表示するかどうかはHTMLの属性と分けておくと、デザインを変えたときに表示の切り替えが壊れにくくなります。ただし、項目のCSSで display: flex などを指定していると hidden が効かなくなるので、[hidden] { display: none !important; } を明示しておくと安心です。

絞り込みの状態はURLだけに持たせ、画面はいつもURLから作り直す。これが崩れにくい作り方です。

条件が複数ある場合の広げ方

カテゴリと価格のように条件が複数ある場合は、パラメータの名前を category と price のように分け、どちらも applyFilter の中で読み取ります。条件ごとに別々の関数を作ると管理が大変になるので、URLを読む関数は1つにしておくのがおすすめです。

チェックボックスで複数の値を選べるようにする場合は、同じ名前のパラメータを並べます。?tag=web&tag=logo のような形です。

JavaScript(チェックボックスの場合)
// 書き込み:チェックの入っている値を、同じ名前で並べる
const params = new URLSearchParams(location.search);
params.delete('tag');
document.querySelectorAll('input[name="tag"]:checked').forEach((box) => {
  params.append('tag', box.value);
});
// → ?tag=web&tag=logo

// 読み取り:getAll で配列として受け取る
const tags = new URLSearchParams(location.search).getAll('tag');
// → ['web', 'logo']

知っておきたい注意点

履歴を増やしたくないときは replaceState

キーワードを1文字入力するたびに絞り込むような作りで pushState を使うと、履歴が1文字ずつ積まれ、戻るボタンを何回も押すことになります。こうした場合は、履歴を増やさずにURLだけを置き換える history.replaceState を使います。選び直す操作は pushState、入力の途中は replaceStateと使い分けると自然です。

URLに想定外の値が来たときの表示を用意する

URLは手で書き換えられますし、以前のカテゴリ名が残ったリンクから開かれることもあります。存在しない値が来ると、どの項目も当てはまらず、一覧が空になります。こうしたときのために、0件のときの表示を用意しておきましょう。0件の画面の作り方は、絞り込み検索で結果が0件のときの表示はどうする?で紹介しています。

まとめ

絞り込み条件のURLへの反映は、URLSearchParams と history.pushState の組み合わせで、ライブラリなしに実装できます。共有・戻るボタン・再訪問の3つのメリットは、事例紹介や商品一覧が充実しているサイトほど実感しやすいので、今ある絞り込みフィルタに加えてみてください。

絞り込み機能そのものの作り方やデモは、商品ページに絞り込み(フィルタリング)機能を追加する方法で紹介しています。

よくある質問(FAQ)

Q. ページを読み込み直すと、絞り込みが元に戻ってしまいます

次の3つを確認してください。1つ目は、ページを開いたときに applyFilter() を呼んでいるか。2つ目は、スクリプトがHTMLより先に動いていないか(defer を付けるか、</body> の直前で読み込みます)。3つ目は、サーバーやCMSのリダイレクトで、URLの ? から後ろが消えていないかです。

Q. 絞り込みごとにURLが増えると、SEOで問題になりませんか?

セレクトボックスなどで切り替えるだけなら、絞り込んだURLへのリンクがページ内にないので、検索エンジンに大量に巡回されることは通常ありません。気になる場合は、ページのcanonical(正規のURLを伝えるタグ)が、条件の付いていない一覧のURLを指しているかを確認しておきましょう。検索結果に出したくないページの扱いは、noindexの解説記事も参考にしてください。

Q. VueやReactなどのフレームワークでも、同じ考え方ですか?

考え方は同じです。フレームワークを使う場合は、ルーティングの仕組みが用意しているクエリパラメータの読み書きの方法に置き換えます。

この記事をシェア
X Facebook LINE

Contact

まず、話してみませんか。

課題が明確でなくても構いません。「こういうことで困っている」というご相談から始めましょう。

無料で相談する →