• 当ページには広告が含まれています

CSSとHTMLで一覧要素を絞り込み表示っぽく見せる

CSSのみで一覧にした要素を絞り込み表示させる
CSSのみで一覧にした要素を絞り込み表示させる

以前に試した仕組みを転用すれば実現できるのでは、と思ったことを試作しました。
CSSだけで簡易に絞り込み表示を行えるように検討しているので、動作に制限はあるもののJSもPHPも不要な内容です。

[追記:2025/12/10]
公開から長期間経過しCSSの機能も増えているためサンプルを新しく作りました。

実現したいこと

実現したいことは以下の通り。

  • CSSだけで絞り込みを行う

一覧で表示されたアイテムを絞り込み表示させる場合、JSを使ってページを移動せずクライアント側で処理をさせたり、PHPなどでサーバー側で用意したページに推移させたりが考えられます。

しかし今回はそういった目的と方法ではなく、CSSで絞り込みと似た見せ方ができないかと試しています。CSSのため画面推移がなく処理待ちもほぼないため、ユーザーにとって利便性もあるのではと考えました。

注意点

  • 動的な一覧取得と絞り込みではなく、あくまで静的HTMLの範囲に限る

今回の試作はAjaxなどで動的に一覧を読み込んでいるわけではなく、あくまでHTML上に現在存在する要素に対してのみの動作です。そのため「絞り込み表示っぽく見せる」というタイトルをつけています。

今後はどうかわかりませんが少なとも現時点では、HTMLとCSSのみで動的な一覧取得と絞り込みを行うことは不可能です。

サンプル

2018年の当記事公開時から比べるとCSSは発展しており、2025年時点での機能を使ったサンプルを作成しました。
基本的にはモダンブラウザなら正常に表示されて動作すると思います。

サンプル1

  • Hタグは見出しに使います。
    HTML
  • ifは条件分岐に使います。
    PHP
  • colorは文字色を操作します。
    CSS
  • =は右の値を左のオペランドに代入します。
    JS
  • 関数内のreturnは実行を停止させます。
    PHP
  • pタグは段落に使います。
    HTML
  • titleタグはページのタイトルに使います。
    HTML
  • varは変数の宣言に使います。
    JS
  • Hタグは見出しに使います。
    CSS
  • aタグはリンクに使います。
    HTML
  • includeはファイルを読み込みます。
    PHP

仕組みに関する補足

長くなるので必要な部分だけ抜き出して不要部分を省略した形で記載します。
以下がHTMLとCSSです。


<style>
  .refine:has(#refine-html:checked) .refine-item:not(.html),
  .refine:has(#refine-css:checked) .refine-item:not(.css),
  .refine:has(#refine-js:checked) .refine-item:not(.js),
  .refine:has(#refine-php:checked) .refine-item:not(.php) {
    display: none;
  }
</style>
<div class="refine">
  <div class="refine-container">
    <div class="refine-aside">
      <div class="refine-buttons">
        <label class="refine-btn">
          <input id="refine-all" type="radio" name="refine3-btn" checked />全表示
        </label>
        <label class="refine-btn">
          <input id="refine-html" type="radio" name="refine3-btn" />HTML
        </label>
        <label class="refine-btn">
          <input id="refine-css" type="radio" name="refine3-btn" />CSS
        </label>
        <label class="refine-btn">
          <input id="refine-js" type="radio" name="refine3-btn" />JS
        </label>
        <label class="refine-btn">
          <input id="refine-php" type="radio" name="refine3-btn" />PHP
        </label>
      </div>
    </div>
    <div class="refine-main">
      <ul class="refine-items">
        <li class="refine-item html">
          <div class="refine-item__contents"><p>Hタグは見出しに使います。</p></div>
        </li>
        <li class="refine-item php">
          <div class="refine-item__contents"><p>ifは条件分岐に使います。</p></div>
        </li>
        <li class="refine-item css">
          <div class="refine-item__contents"><p>colorは文字色を操作します。</p></div>
        </li>
        <li class="refine-item js">
          <div class="refine-item__contents"><p>=は右の値を左のオペランドに代入します。</p></div>
        </li>
      </ul>
    </div>
  </div>
</div>

以前は兄弟セレクタの~を使っておりHTMLの構造に制限がありましたが、現在では:has()が利用できます。

具体的には、前述のサンプルで「HTML」が選ばれていると仮定した場合、以下のような指定を行うことでボタンと非表示対象を連動させています。


  .refine:has(#refine-html:checked) .refine-item:not(.html) {
    display: none;
  }
  • 「指定の状態の要素を持つ親(=.refine:has(#refine-html:checked))」の中にある、指定クラスが付いてない.refine-item(=.refine-item:not(.html))を非表示にする

言葉で説明するとややこしいですが、親子関係を利用する方法です。

兄弟セレクタから解放されたことでサンプルのように2カラムのレイアウトもできます。
親要素の範囲であれば上でも下でも、positionなどを使って重ねるなどでも、好きな場所に絞り込みボタンを配置可能です。

サンプル2

  • Hタグは見出しに使います。
    HTML
  • ifは条件分岐に使います。
    PHP
  • colorは文字色を操作します。
    CSS
  • =は右の値を左のオペランドに代入します。
    JS
  • 関数内のreturnは実行を停止させます。
    PHP
  • pタグは段落に使います。
    HTML
  • titleタグはページのタイトルに使います。
    HTML
  • varは変数の宣言に使います。
    JS
  • Hタグは見出しに使います。
    CSS
  • aタグはリンクに使います。
    HTML
  • includeはファイルを読み込みます。
    PHP

仕組みに関する補足

選択対象以外の消すのではなく、表示を弱めるパターンです。
HTMLは同じなので、該当箇所のCSSのみ記載します。


  .refine-item {
    transition: opacity .3s;
  }

  .refine:has(#refine-html:checked) .html,
  .refine:has(#refine-css:checked) .css,
  .refine:has(#refine-js:checked) .js,
  .refine:has(#refine-php:checked) .php {
    border-color: #aaa;
    box-shadow: 0 0 5px 0 rgb(0 0 0 / .2);
  }

  .refine:has(#refine-html:checked) .refine-item:not(.html),
  .refine:has(#refine-css:checked) .refine-item:not(.css),
  .refine:has(#refine-js:checked) .refine-item:not(.js),
  .refine:has(#refine-php:checked) .refine-item:not(.php) {
    opacity: .3;
    filter: blur(2px);
  }

レガシーなブラウザを切り捨てる前提ですが、ぼかしなどもCSS単独で実装可能なのでリッチな表現が手軽に実現できます。

結び

当記事公開が2018年なので随分と時間が経っていますが、2025年現在でもCSSの開発は進んでいます。
HTMLの進化も進み、JavaScriptなしのHTML+CSSでユーザーのアクションに対応するパーツ(カルセールやモーダル)なども作れるようになってきました。

今後もHTML+CSSで実現可能なものは増えていくと思いますので、HTMLとCSSを学んでも無駄にはならないと思います。

旧版の内容

記録用に旧版を残しています。

旧版の内容

サンプル1

以下がサンプルで上部のボタンを押せば動作します。

なお、画面サイズに合わせた調整はしていないため、スマホサイズの画面では見づらい感じになっていると思います。

Hタグは見出しに使います。
HTML
ifは条件分岐に使います。
PHP
colorは文字色を操作します。
CSS
=は右の値を左のオペランドに代入します。
JS
関数内のreturnは実行を停止させます。
PHP
pタグは段落に使います。
HTML
titleタグはページのタイトルに使います。
HTML
varは変数の宣言に使います。
JS
borderは枠線を操作します。
CSS
aタグはリンクに使います。
HTML
includeはファイルを読み込みます。
PHP

以下HTML。


<div class="refine">
  <input id="refine-0" type="radio" name="refine-btn" checked>
  <label class="refine-btn" for="refine-0">全表示</label>
  <input id="refine-1" type="radio" name="refine-btn">
  <label class="refine-btn" for="refine-1">HTML</label>
  <input id="refine-2" type="radio" name="refine-btn">
  <label class="refine-btn" for="refine-2">CSS</label>
  <input id="refine-3" type="radio" name="refine-btn">
  <label class="refine-btn" for="refine-3">JS</label>
  <input id="refine-4" type="radio" name="refine-btn">
  <label class="refine-btn" for="refine-4">PHP</label>

  <div class="refine-teims html"><p>Hタグは見出しに使います。</p><span class="refine-tag">HTML</span>
  </div>
  <div class="refine-teims php"><p>ifは条件分岐に使います。</p><span class="refine-tag">PHP</span>
  </div>
  <div class="refine-teims css"><p>colorは文字色を操作します。</p><span class="refine-tag">CSS</span>
  </div>
  <div class="refine-teims js"><p>=は右の値を左のオペランドに代入します。</p><span class="refine-tag">JS</span>
  </div>
  <div class="refine-teims php"><p>関数内のreturnは実行を停止させます。</p><span class="refine-tag">PHP</span>
  </div>
  <div class="refine-teims html"><p>pタグは段落に使います。</p><span class="refine-tag">HTML</span>
  </div>
  <div class="refine-teims html"><p>titleタグはページのタイトルに使います。</p><span class="refine-tag">HTML</span>
  </div>
  <div class="refine-teims js"><p>varは変数の宣言に使います。</p><span class="refine-tag">JS</span>
  </div>
  <div class="refine-teims css"><p>Hタグは見出しに使います。</p><span class="refine-tag">CSS</span>
  </div>
  <div class="refine-teims html"><p>aタグはリンクに使います。</p><span class="refine-tag">HTML</span>
  </div>
  <div class="refine-teims php"><p>includeはファイルを読み込みます。</p><span class="refine-tag">PHP</span>
  </div>
</div>

以下CSSですが、レイアウト関連は後述しますのでここでは肝心の動作部分だけ記述します。


input[name='refine-btn'] {
  position: relative;
  display: none;
}
input[name='refine-btn']:checked + label {
  color: #fff;
  background-color: #db6c34;
}
#refine-1:checked ~ .refine-teims:not(.html),
#refine-2:checked ~ .refine-teims:not(.css),
#refine-3:checked ~ .refine-teims:not(.js),
#refine-4:checked ~ .refine-teims:not(.php) {
  display: none;
}

サンプル2

別バージョン。

Hタグは見出しに使います。
HTML
ifは条件分岐に使います。
PHP
colorは文字色を操作します。
CSS
=は右の値を左のオペランドに代入します。
JS
関数内のreturnは実行を停止させます。
PHP
pタグは段落に使います。
HTML
titleタグはページのタイトルに使います。
HTML
varは変数の宣言に使います。
JS
borderは枠線を操作します。
CSS
aタグはリンクに使います。
HTML
includeはファイルを読み込みます。
PHP

クラスやnameの名称以外のHTML構造は同じなのでCSSだけ。


input[name='refine2-btn'] {
  position: relative;
  display: none;
}
input[name='refine2-btn']:checked + label {
  color: #fff;
  background-color: #db6c34;
}
#refine2-1:checked ~ .html,
#refine2-2:checked ~ .css,
#refine2-3:checked ~ .js,
#refine2-4:checked ~ .php {
  border-color: #db6c34;
}
#refine2-1:checked ~ .refine2-teims:not(.html),
#refine2-2:checked ~ .refine2-teims:not(.css),
#refine2-3:checked ~ .refine2-teims:not(.js),
#refine2-4:checked ~ .refine2-teims:not(.php) {
  opacity:.5;
}

参考サイト

この記事の仕組みは、以前に以下のページの情報を参考にして案件対応した時に思いついたものです。

この方法は「指定した要素を表示させる」という趣旨ですが、これができるなら「指定した要素以外を非表示にする」もできるなと。

問題点

いくつか問題点はありますが、一番面倒なのは「CSSで装飾がやりにくい」ということです。

兄弟要素を指定する仕組みの都合上、ボタンとなるlabelタグをdivタグなどで囲うことができず、そのまま続けて操作対象要素を並べなければなりません。

当記事のサンプルのように横に複数並べるタイプでは無理矢理なCSS設定をしないとなかなか整えられませんでした。

一応ですが、以下がその部分のCSSです。


.refine {
  display: flex;
  flex-wrap: wrap;
}
.refine-btn, .refine-teims {
  box-sizing: border-box;
  margin: 0 4px 10px 4px;
  border-style: solid;
  border-width: 1px;
}
.refine-btn {
  font-size: 14px;
  width: calc(20% - 8px);
  padding: 3px 0;
  text-align: center;
  border-radius: 3px;
  background-color: #fff;
  border-color: #999;
}
.refine-teims {
  position: relative;
  width: calc(25% - 8px);
  padding: 5px;
  border-color: #ccc;
}
.refine-teims p {
  margin: 0 0 2em 0;
}
.refine-tag {
  font-size: 14px;
  position: absolute;
  bottom: 5px;
  display: block;
  width: calc(100% - 10px);
  text-align: center;
  background-color: #eee;
}

ボタンだけが並ぶサイズにwidthを設定することで、divタグなどを使わずに見た目を整えている状態です。

内容や最終的なデザインによってはdisplay:flex;を使わない方が簡易にできるかもしれず、CSSの作り方は適宜検討が必要になります。

結び

一応、当サイトの以下のカテゴリーページで試しに付けています。

見られる場合は、サムネイルの数が多いため表示が重い可能性に留意してください。小さなサイズのサムネイルとはいえさすがに数が多いとそれなりになりますので。

WPのタグを利用しているため事前準備が多少必要ですが、肝心の仕組み部分は同じです。

仕組みよりもレイアウトやデザインで苦労をする手法だなと実感しました..。

補足

今回のサンプルの動作は以下のようにいえるかもしれません。

  • PC閲覧時には、多くの要素が見える都合上、他の要素の位置に影響を与えない点から、サンプル2が良い
  • スマホ閲覧時には、縦一列に並べる前提で、不要な要素を消せば縦方向のスクロール量を減らせる点から、サンプル1が良い

割と使えそうな気配もしてきましたが、いかんせんCSSの当て辛さは残ったままなので微妙です。

58人がこの記事を評価

役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。

連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。

コメント欄