BLOG
25年新卒 WEBエンジニア インターンシップ 受付中

CSSの便利な新セレクタ :has() / :is() / :where() の使い方解説

更新日:2023/06/23


CSSは常に進化を続けていて、開発者にとってより便利な新しいセレクタも登場してきています。
その中でも特に便利なセレクタが、:has()、:is()、、:where()の3つです。
この3つの新しいセレクタは、「子要素を持つかどうかの判定」や「詳細度を上げない」といった待望の仕様を備えており、CSSの開発効率を向上させる強力なツールになるでしょう。

本記事では、それぞれのセレクタの使い方と具体的な使用例をデモサイトを交えて解説します。
それではどうぞ!

:has()

今まで、特定の要素の存在のあり・なしをCSSで判別する方法は、「それぞれのクラスを作成して調整する方法」または、「JSで調整する方法」しかありませんでした。
しかし:has()セレクタは、指定したセレクタ内に特定の要素が存在する場合にCSSを適用できます。
子要素や隣接要素があるかどうか、またはその状態(ホバー・フォーカス等)に応じて、要素のスタイルを変えることが可能になったということです。これは待ち望んでいた人も多い機能ではないでしょうか。

■参考ドキュメント
:has() | MDN

使用例

主な使い方としては以下の3パターンが挙げられます。

  • 特定の要素がセレクタ内にあるかどうか判別する

  • 特定の要素がある要素の後に続いているかどうか判別する

  • form要素でフォーカスされたinput要素があるか判別する

具体的な書き方は以下の通りです。

特定の要素がセレクタ内にあるかどうか判別する

一例として、カードレイアウトで画像がある場合・ない場合で条件分岐をしてCSSの適用する方法をご紹介します。
画像がある場合はテキストと横並びになるように調整します。

デモはこちらです。
:has – 特定の要素がセレクタ内にあるかどうか判別する

▼HTML

ソースの表示

▼CSS

ソースの表示

ポイントは以下です。

.cardの中に.card-imgがあるときだけ、.cardの中身を横並びにしています。
それに合わせて、テキストが増えても段落ちしないようにテキストエリアの横幅も画像と余白分を除いた幅を指定します。

これで画像がある場合、中身が横並びになり、テキストの横幅も画像+余白分を除いたサイズになりました。
画像がない場合のテキストは親要素いっぱいの横幅になっていると思います。

特定の要素がある要素の後に続いているかどうか判別する

親要素の中に特定の要素が含まれているか判別できるだけではなく、ある要素の後に特定の要素が続いているかどうかも判別することができます。

デモはこちらです。
:has – 特定の要素がある要素の後に続いているかどうか判別する

▼HTML

ソースの表示

▼CSS(※追加された部分だけ記載しています)

ソースの表示

ポイントは以下です。

.card-descriptionの後に、.card-descriptionか.card-linkがある時、下余白10pxがつきます。

form要素でフォーカスされたinput要素があるか判別する

form要素でフォーカスされたinput要素があるかどうか判別することも可能です。

デモはこちらです。
:has – form要素でフォーカスされたinput要素があるか判別する

▼HTML

ソースの表示

▼CSS

ソースの表示

ポイントは以下です。

form要素に含まれたinputがフォーカスされたら、form要素のドロップシャドウを青色に変更しています。

対応ブラウザ

:has()セレクタのサポート状況は以下です。
2023年6月現在は、Chrome・Edge・Safari・Operaでサポートされています。

:is()

:is()セレクタは、指定した要素に一致する場合にCSSを適用できます。
特定の要素をまとめて選択し、一括でスタイルを指定することができるので、冗長なコードを短くすることができます。

■参考ドキュメント
:is() | MDN

使用例

デモはこちらです。
:is – まとめて指定

具体的な書き方は以下の通りです。

例えばh1、h2、h3の色を青色にしたい時、それぞれCSSを書いていくと同じ内容が複数になり、コードが冗長になってしまいます。
そこで:is()セレクタを使ってまとめて指定することでコードを短く綺麗にまとめることができます。

▲ 同じ記述が続いて長い…。

▲ たった3行!

Scssを使っている場合

CSSで:is()セレクタを使用した場合とScssでネストを使用した場合、表示上の得られる結果は同じです。
コードの分かりやすさ・管理しやすさの面でいうと、Scssではネストを使用するほうがよいかもしれません。

ただ、Scssのネストで書いた場合、生成されたCSSでは同じセレクタが繰り返されるのでその分冗長になります。

▲ Scssのネスト

CSSが生成されると…。

▲ 要素ごとに.wrapが生成されてしまう

その点、:is()セレクタは、複数の要素をまとめて指定することができます。

▲ :is()でまとめる

CSSが生成されると…。

▲ .wrapは一つだけ!

Scssから生成されたCSSの無駄を省きたい場合は、:is()セレクタを使ってまとめるのも一つの手です。

対応ブラウザ

:is()セレクタのサポート状況は以下です。
2023年6月現在は、Chrome・Edge・Safari・Firefox・Operaでサポートされています。

:where()

:where()セレクタは、:is()セレクタと同じように特定の要素をまとめて選択し、一括でスタイルを指定することができるセレクタです。
しかし、:is()セレクタと違うのは「詳細度(CSSの優先度)の高さ」です。
:where()セレクタを使うと詳細度が0となり、優先順位が一番低くなるので、スタイルを上書きしたいときに便利なセレクタです。

■参考ドキュメント
:where() | MDN

詳細度については、以下のブログで詳しく紹介しているので合わせてご覧ください。
CSSの詳細度と継承について

使用例

デモはこちらです。
:where – まとめて指定

具体的な書き方は以下の通りです。

書き方は:is()セレクタと同じです。

▲ 同じ記述が続いて長い…。

▲ たった3行!

Scssを使っている場合

Scssのネストが深くなっていくと、スタイルの詳細度が上がり上書きが難しくなってしまいます。
しかし、:where()セレクタを使用すると詳細度が0になるので、深くなったネストでも上書きすることができます。

上記のコードがあったとします。

❶.wrap内の.textに含まれるaタグの色は青色
❷.wrap内に含まれているaタグは赤色
という指定すると階層が深いのは❶になるので、.text内のリンクは青文字になります。

しかし、以下のように:where()セレクタを使って書くと階層の深さ関係無く、詳細度が一番低くなるので❷のCSSが当たるようになります。

とはいえ、:where()セレクタ使えるからと言ってネストを深くしすぎないように注意が必要です。
状況によって使い分けて行くことが大事です。

対応ブラウザ

:where()セレクタのサポート状況は以下です。
2023年6月現在は、Chrome・Edge・Safari・FireFox・Operaでサポートされています。

:is()と:where()の違い

先ほど詳細度の話が出てきましたが、実際に詳細度での違いというのがどのように影響するのか見てみたいと思います。
同じ構造のHTMLに、それぞれ:is()、:where()でCSSを指定してみましょう。

デモはこちらです。
:is()と:whereの違い

例えば、以下のようなコードがあるとします。

ソースの表示

.is-wrapには:is()を使って、.where-wrapには:where()を使ってリンクの色を指定します。

指定した色にリンクの色が変わりました。
しかし途中で.bottomのリンクだけ色を上書きたい、と最後のリンクの色を上書きするCSSを書くとどうなるでしょう?

結果上書きされたのは、.where-wrapの.bottomに含まれるリンクだけなのがわかります。
最後のリンクの色を上書きするCSSの詳細度が:where()セレクタの詳細度より高いので、.where-wrapの.bottomに含まれるリンクだけ緑色に変わりました。

まとめ

本記事では、CSSの最新バージョンで追加された便利な新セレクタ、:has()、:is()、:where()の使い方についてデモサイトを交えて紹介しました。
これらを上手に使うことで、コードを短く保ちながら柔軟性を高めることができます。

今回ご紹介したのは一例ですが、他にも様々な使用方法があると思います。
セレクタを使いこなして、効率的かつ柔軟なスタイリングを実現しましょう!

FOLLOW US