CSSって難しい! セレクタ「.a .b .c」と「.a :is (.b . c)」は同じに見えるけど実は違う、ブラウザはセレクタを右から左に読む
Post on:2023年1月31日
CSSの:is()
疑似クラスが各ブラウザにサポート(参考: Can I use)されるようになって、1年が過ぎました。使用率も97%を超え、通常の案件に使用している人も多いと思います。
:is()
疑似クラスは複数のセレクタを1つにまとめられて便利ですが、セレクタの末尾に使用すると、予想よりも多くの一致が発生するかもしれません。どんな場合にそうなるのかを紹介します。
たとえば、下記の.a .b .c
と.a :is (.b . c)
は同じように見えるかもしれませんが、実は異なります。
Using :is() in complex selectors selects more than you might initially think
by Bramus
CSSの:is()
疑似クラスやセレクタの読む順番について詳しくは、以前の記事をご覧ください。
- CSSの新しい疑似クラス:is()と:where() なんだこれ便利すぎる!
- CSSの新しい疑似クラス関数「:is()」複数のセレクタを1つにまとめることができて、これは便利
- CSSについて誰も私に教えてくれなかった大切なこと -プロパティやセレクタがパフォーマンスに与える影響
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
is()にするか、しないか?
次のような複合セレクタをご覧ください。
1 2 3 |
.a .b .c { background: green; } |
そして、:is()
を使用したセレクタ。
1 2 3 |
.a :is(.b .c) { background: green; } |
この2つのセレクタは同じように見えるかもしれませんが、動作が異なります。ブラウザはセレクタを右から左に読み、2番目のセレクタはより多くのものを選択します。
デモページ
以下のデモでは、ネストされた2つのマークアップがあります。
1 2 3 4 5 6 7 8 9 10 11 |
<div class="a"> <div class="b"> <div class="c"></div> </div> </div> <div class="b"> <div class="a"> <div class="c"></div> </div> </div> |
下記のデモページでドロップダウンから.a .b .c
または.a :is (.b . c)
を選択して、どの要素に一致するかご覧ください。
See the Pen
Using :is() in complex selectors selects more than you might initially think by Bramus (@bramus)
on CodePen.
2つのセレクタの違いを解説
ブラウザは、CSSのセレクタを右から左に読みます。つまり、複合セレクタの場合、ブラウザは最後のユニットから開始して、チェーンを上に移動します。
.a .b .c
このセレクタには、.a
, .b
, .c
の3つのユニットがあります。ブラウザは一致する要素を見つけようとする場合、最初にすべての.c
要素を探し、次に.b
の親要素を持っているかを確認します。そして、.b
が.a
要素の子であるかどうかを確認します。
- 最初に、すべての
.c
要素を探す。 .c
要素が.b
の親要素を持っているかを確認。- その一致した
.b
要素が.a
の親要素を持っているかを確認。
.a :is(.b .c)
このセレクタには、.a
, :is(.b .c)
の2つのユニットがあります。最初に評価されるユニット:is(.b .c)
は、.b
の親要素を持つ.c
要素と一致します。これがtrueであれば、ブラウザは続けてその一致した要素が.a
の親要素を持っているかどうかを確認します。
- 最初に、
:is(.b .c)
を評価。 .b
の親要素を持つ.c
要素を探す。- その一致した
.c
要素が.a
の親要素を持っているかを確認。
つまり、.a :is(.b .c)
は先ほどのスニペットのすべての.c
要素に一致します。これはセレクタが「.b
と.a
の両方を含む.c
要素を探してください」とブラウザに伝えるからです。
さらに、下記の場合も一致します。
1 2 3 |
<div class="a b"> <div class="c"></div> </div> |
なぜ、このようなことが必要なのか?
私自身はこのようなセレクタを書くつもりはありませんが、CSS Nesting ModuleでCSSのネストが使用できるようになるため、非常に関連性があります。CSS Working Groupで、CSSのネストは:is()
を使用して&
を展開すべきかどうか活発な議論がされています。
ネストされたブロックを見てましょう。
1 2 3 4 5 |
.b .c { .a & { background: green; } } |
現在の仕様で規定されているように、:is()
で囲まれた外側のセレクタに&
を展開すると、このスニペットは次のようになります。
1 2 3 |
.a :is(.b .c) { background: green; } |
これは、Sassや他のプリプロセッサを使用したことがある制作者にとっては直感に反するかもしれません。Sassでは単に&
を外側のセレクタに置き換えるだけです。
1 2 3 |
.a .b .c { background: green; } |
この記事で示したように、これらは異なる挙動を示します。
:is()について他に何かありますか?
以前の記事で解説したように、:is()
について知っておくべきことは他にもあります。
:is()
のセレクタリストは、寛容である。:is()
の詳細度は、引数の詳細度である。:is()
は、疑似セレクタでは機能しない(今のところ)。
詳しくは、Three important things you should know about CSS :is()をご覧ください。
sponsors