CSS大解剖 12日目: 「セレクター 2/3」

に公開

本稿は、2024年2月頃に書き溜めていたシリーズです。最後まで温存させるのが勿体ないので、未完成ですがそのまま公開します(公開日: 2025/9/25)。そのため、内容の重複や記述方針の不一致があるかもしれませんが、ご理解ください。


CSSの仕様を理解するために、1日ごとにテーマを決めて説明する企画12日目です。今日のテーマは前回に引き続き「セレクター」です。

単純セレクター

前述の通り、擬似要素以外の単純セレクターは要素の絞り込みを行います。

要素型セレクターと全称セレクター

要素型セレクターと全称セレクターは、名前空間タグ名による絞り込みを行います。

自動 全ての名前空間 指定した名前空間 名前空間のない場合にマッチ
要素型セレクター p *|p html|p |p
全称セレクター * *|* html|* |*

CSSの場合、名前空間は@namespaceであらかじめ宣言し、名前を割り当てておくと使えます。他の文脈で名前空間を利用可能かは、その文脈ごとに異なります。HTML文書で要素の名前空間として必要なのは、基本的に以下の3種類に限られるでしょう

  • HTML名前空間 http://www.w3.org/1999/xhtml
  • MathML名前空間 http://www.w3.org/1998/Math/MathML
  • SVG名前空間 http://www.w3.org/2000/svg

@namespace は名前つきの名前空間だけではなく、デフォルト名前空間を指定することもできます。デフォルト名前空間が設定されている場合、上記の「自動」はその名前空間に解決されます。デフォルト設定がなければ、全ての名前空間にマッチします。

@namespace "http://www.w3.org/1999/xhtml";
/* HTMLのtitle要素にマッチし、SVGのtitle要素にはマッチしない */
title {}
/* HTMLとSVGの両方にマッチする */
*|title {}

全称セレクターはいずれも詳細度に寄与しません。要素型セレクターは (0, 0, 1) の寄与を持ちます。

暗黙の全称セレクター

合成セレクターが要素型セレクターも全称セレクターも含まない場合、先頭に * が指定されたものとみなされます。たとえば、 .foo.bar*.foo.bar とみなされます。

この規則には以下の副作用があります。

  • 無貌な要素や擬似要素にマッチしなくなる。
  • デフォルト名前空間が指定されている場合、その名前空間以外の要素にマッチしなくなる。

しかし、この規則を適用すると辻褄が合わなくなる仕様もあるため、この規則には何らかの例外条件があると考えられます。この条件は現行規格では厳密に定義されていませんが、おおむね以下の場合には * が適用除外されると考えられます。

属性類セレクター

属性類セレクターには以下の3種類があります。

  • 属性セレクター [open]
  • クラスセレクター .btn
  • IDセレクター #main

これらはいずれも、要素を属性値に基づいて選択するものです。クラスセレクターとIDセレクターはそれぞれ class 属性 / id 属性を参照します[1]

クラスとIDはいずれも要素を特定するための汎用的な仕組みとして使えますが、クラスは多対多対応なのに対してIDは一対一対応であるという違いがあります。つまり、

  • 1つの要素に複数のクラスを割り当てることができます。
  • いっぽう、1つの要素には高々1つのIDのみを割り当てることができます。
  • 1つのクラスを複数の要素に割り当てることができます。
  • いっぽう、1つのIDは1つの要素にのみ割り当てられることができます。ただし、これは技術的には強制されていません。

加えて、IDセレクターは詳細度の計算が異なります。属性セレクターとクラスセレクターの詳細度の寄与が (0, 1, 0) なのに対しIDセレクターの詳細度の寄与は (1, 0, 0) です。

擬似クラス

擬似クラスセレクターは、セレクター構文のなかで最も汎用的な仕組みであり、原理的には(仕様策定側がその気になれば)ほとんど何でも記述することができます。実際、セレクターの高度な機能のほとんどは擬似クラスか擬似要素を使って定義されています。

擬似クラスには識別子形式 (e.g. :first-child) と関数形式 (e.g. :nth-child()) の2種類があり、後者が擬似クラスを表現力最強の座に押し上げています。CSS文法の特性上、 () の中身のパースは後回しにできるようになっており、この中の文法は擬似クラスの種類ごとに独立に定義されています。

擬似クラスの本来の意図は、要素をDOM上のその要素の情報以外から選択するための補助的な機能というものです。たとえば、 :hover はマウスポインタの下にある要素を選択しますが、「マウスポインタの下にある」という情報は要素内に記録されているわけではなく、それを表示するWebブラウザの一時的な状態として存在しているだけです。このような「普通の擬似クラス」の詳細度寄与はクラスセレクターと同じ (0, 1, 0) です。

論理結合擬似クラス

論理結合擬似クラス:is(), :not(), :where() の3種類です。 (:has() はやや性質が異なるため、あとで説明します)

  • :is() はJavaScriptにおける () のようにセレクターのグルーピングを行う擬似クラスです。 :is() の中にはセレクターリスト (OR演算) を書くことができるため、 :is(a[any-link], button, input[type="button" i])::hover のように共通部分をまとめたいときに有用です。
    • 古い資料では :matches() と呼ばれていることもあります。
  • :not():is() の結果を最後に真偽反転するものです。NOT演算が必要な場合に使います。
  • :where():is() と同じ挙動ですが、詳細度を無視するよう定められているため、詳細度のコントロールに利用可能です。

これらに共通して、以下の点に注意が必要です。

  • :is(), :not() の詳細度は、セレクターリスト内の複素セレクタの詳細度のうち最大のものとして計算されます。
  • いずれも現行仕様では擬似要素にはマッチしないよう定められています。
  • :is(), :where() には無効なセレクターに対する特殊な扱いが定義されています。これについては後述します。

結合子

結合子 は合成セレクター同士を接続する演算子で、右辺の要素を左辺要素との関連性によりフィルターします。

主要な結合子は以下の4種類です。

  • 子孫結合子
  • 子結合子 >
  • 次兄弟結合子 +
  • 後続兄弟結合子 ~

それぞれの結合子の役割は、およそその名前からわかる通りです。註釈が必要だとしたら以下のような点でしょうか。

  • 子孫結合子以外の結合子を利用する場合は、前後の空白はあってもなくても構いません。
  • 子孫結合子と後続兄弟結合子は自分自身を選択しません。たとえば、 #foo #foo#foo ~ #foo のようにしても #foo 自身を選択することはありません。
  • 次兄弟結合子において、前の要素と後の要素の間には要素以外のノード (テキストなど) があっても構いません。
  • 次兄弟結合子の + という記号は視覚的にとても紛らわしいですが、他の結合子と同じく、左右不対称です。

脚注
  1. 仕様上、HTML以外の文書に適用される場合は他の属性を参照してもいいことになっています。しかし、わざわざ別の属性名を利用する必要性はほぼないでしょう。 ↩︎

Discussion