疑似要素と疑似クラスを一挙にまとめてみる

# はじめに

CSSセレクタである疑似要素と疑似クラスは有用なものが多いですが、数多く存在するため覚えきれていないという方も少なくないはずです。

今回は、この2つのセレクタについてまとめていきます。
(Experimentalな機能については除外します。)

出来るだけアルファベット順に並べていますが、クラスの意味によって順番が入れ替わっていますのでご了承ください。

# 疑似？擬似？

:::note warn
ゲシュタルト崩壊しそうな人は読み飛ばしていただいて構いません。
結論としては「疑似」にしとこって感じです
:::

検索すると恐らく「擬似要素・擬似クラス」としているサイトが多いかもしれませんが、実はMDN Docsの中でも表記が揺れています。
意味合いもほとんど同じなようで、英語ではPseudoと表しますが、これの訳を見ても「擬似／疑似」と書いてあったりします。

では一体どちらで表記するのが正しいのでしょうか？

ざっと調べてみた感じ、「**疑似**」が**漢語としては正しい**ようです。

「擬似」は恐らく日本人が作った言葉なのではないか、とのこと。

ということで本記事では「疑似」と記載します。
（実生活でも疑似を使うようにしようと思いました（小並））

# 疑似クラス

疑似クラスは、その要素の**状態**に基づいてスタイルを付与することができるものです。
CSSで記述する際は、`:hover`のように、コロンを1つつけます。

```css:構文
selector:pseudo-class {
  property: value;
}
```

## ユーザ操作

### `:active`

アクティブ化された要素(`a`や`button`)を指定します。
一般的にクリックボタンを押下したときにアクティブ状態となります。

リンク関連の疑似クラスである`:link`、`:hover`、`:visited`によって上書きされるため、`:link`→`:visited`→`:hover`→`:active`の順(LVHA順)で記述するのが良いでしょう。

### `:focus`

フォーカスが出来る要素をクリックまたはTabキーで選択したときに一致します。

:::note
フォーカスを持つ要素を**含む**要素を選択したい場合は、`:focus-within`を使います
:::

### `:focus-visible`

要素が`:focus`の条件に一致しているとき、UAがフォーカスしていることを明示するべきと判断された場合に適用されます。
マウスでクリックしたときはいらないけど、キーボードのTabキーで選択したときはフォーカス表示してほしいときなどに使います。

### `:focus-within`

その要素または子孫要素にフォーカスを持つ場合に一致します。

### `:hover`

カーソルを要素の上にかざしたときに一致します。

:::note warn
タッチデバイスでの使用はしばしば問題になるため考慮が必要です。
:::

## ツリー構造

### `:first-child`

兄弟要素の中で一番最初の要素と一致します。

### `:last-child`

兄弟要素の中で一番最後の要素と一致します。

### `:only-child`

兄弟要素がない要素と一致します。

### `:nth-child()`

兄弟要素の中で指定した位置と一致します。

### `:nth-last-child()`

兄弟要素の中で指定した位置と一致します。
後ろから前にカウントしていきます。

### `:nth-of-type()`

兄弟要素の指定した要素の中で指定した位置と一致します。

#### `:nth-of-type()`と`:nth-child()`の違い

`:nth-of-type()`と`:nth-child()`には、指定の要素以外の要素をカウントするかどうかの違いがあります。
前者がカウントせず、後者はカウントします。
以下の例を見てみましょう。

```html
<section>
  <h2>title</h2>
  <p>text content<p>
  <span>category<span>
  <span>author<span>
  <span>date<span>
</section>
```

このようなHTMLがあった場合、それぞれの指定は以下のように選択されます。

```css: categoryのspanを選択
span:nth-child(3) {
  color: red;
}
```

```css: dateのspanを選択
span:nth-of-type(3) {
  color: red;
}
```

つまり、こういうことになります。
`span:nth-child(3)`は単純に兄弟要素を上から数えて3番目、
`span:nth-of-type(3)`は兄弟要素中のspanの中で上から数えて3番目、となります。

```html
<section>
  <h2>title</h2>
  <p>text content<p>
  <span>category<span>  <!-- span:nth-child(3) -->
  <span>author<span>
  <span>date<span>  <!-- span:nth-of-type(3) -->
</section>
```

### `:nth-last-of-type()`

兄弟要素の指定した要素の中で指定した位置と一致します。
後ろから前にカウントしていきます。

### `:first-of-type`

兄弟要素の指定した要素の最初の要素と一致します。

### `:last-of-type`

兄弟要素の指定した要素の最後の要素と一致します。

### `:only-of-type`

同じタグの兄弟要素がない要素と一致します。

### `:defined`

定義されているすべての要素と一致します。

### `:empty`

要素ノードや文字列などの子を持たない要素と一致します。

### `:host`

Shadow DOM[^1]のルートホスト要素と一致します。
（Web Componentsについて理解していないと十分に理解できないかもしれません。）

[^1]: Shadow DOM: Light DOMから独立したツリー構造

```html
<custom-elem class="foo">
  <div class="foo"></div>
</custom-elem>
```

Shadow DOM内からルートホストを選択したいときには以下のようにします。
この場合、ルートである`custom-elem`を選択することになります。

```css
:host {
  /* styles */
}
```

### `:host()`

Shadow DOMのルートホストのうち、セレクタの引数と一致するものを選択します。

```html
<custom-elem class="foo">
  <div class="foo"></div>
</custom-elem>
```

```css
:host(.foo) {
  /* styles */
}
```

### `:is()`

:::note
`:matches()`から`:is()`に変わりました
:::

引数にセレクタリストを取り、リスト中のいずれかに当てはまる要素と一致します。

```css
header a:hover,
main a:hover,
footer a:hover {
  opacity: 0.8;
}

/* :is()で書き換えるとこんな感じ */
:is(header, main, footer) a:hover {
  opacity: 0.8;
}
```

### `:where()`

引数にセレクタリストを取り、リスト中のいずれかに当てはまる要素と一致します。

```css
header a:hover,
main a:hover,
footer a:hover {
  opacity: 0.8;
}

/* :is()で書き換えるとこんな感じ */
:where(header, main, footer) a:hover {
  opacity: 0.8;
}
```

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

`:is()`は引数内で最も詳細度の高いセレクタの詳細度になるのに対し、`:where()`は**詳細度が常に0**になります。

### `:not()`

引数に指定されたセレクタに一致しない要素を選択します。

使用する際いくつか注意点があります。

- 入れ子にはできない
- ルールの詳細度を上げることができる
  - `#foo:not(#bar)`は`#foo`の詳細度より高くなる
- `:not(.foo)`は「`.foo`ではない要素」のため、`<html>`や`<body>`を含む
- 祖先要素を除外できない
- 複数セレクタを指定する書き方はブラウザが未対応の場合がある
  - `:not(.foo):not(.bar)`と書く

### `:root`

ツリーのルート要素を選択します。
HTMLでは`<html>`要素を表しますが、`html`セレクタより詳細度が高くなります。

## フォーム

### `:autofill`

`<input>`要素がブラウザによって自動補完されたときに一致します。
そのフィールドをユーザが編集すると一致しなくなります。

:::note warn
多くのブラウザで`:-webkit-autofill`で`background-color`、`background-image`、`color`に対して`!important`でスタイルを定義しているため、CSSでは独自でスタイルが上書きできません。
:::

### `:checked`

ラジオボタン、チェックボックス、オプションボタン要素が選択された状態と一致します。

### `:default`

以下に一致します。

- フォームのデフォルトボタンである送信ボタン`<button>`
- `<input type="checkbox">`
- `<input type="radio">`
- `selected`属性を持つ`<option>`

### `:enabled`

アクティブ化したりフォーカスができる要素と一致します。
例えば、`disabled`属性が与えられた`input`要素には一致しません。

### `:disabled`

`:enabled`の逆です。

### `:indeterminate`

未確定状態にあるフォーム要素と一致します。

- `radio`要素で、同じ`name`の値を持つ全てのラジオボタンが未選択状態のとき
- `checkbox`要素で、JavaScriptで`indeterminate`が`true`に設定されているとき
- `<progress>`要素で、途中のとき

### `:in-range`

`<input>`要素で入力値が`min`と`max`属性による**範囲内**にあるものに一致します。

### `:out-of-range`

`<input>`要素で入力値が`min`と`max`属性による**範囲外**にあるものに一致します。

### `:optional`

`required`属性が設定されていない`<input>`、`<select>`、`<textarea>`要素と一致します。

### `:placeholder-shown`

プレースホルダが表示されている`<input>`、`<textarea>`要素と一致します。

### `:read-only`

読み取り専用`readonly`が指定されている要素と一致します。

### `:read-write`

ユーザが編集可能な要素と一致します。

### `:required`

`<input>`、`<select>`、`<textarea>`要素で`required`属性が与えられているものと一致します。

### `:valid`

バリデーションが成功したフォーム要素と一致します。

### `:invalid`

バリデーションが失敗したフォーム要素と一致します。

## リンク関連

### `:any-link`

`href`属性を持つ全ての`<a>`、`<area>`と一致します。

### `:link`

`href`属性を持つ`<a>`、`<area>`のうち、未訪問の要素と一致します。

### `:visited`

`href`属性を持つ`<a>`、`<area>`のうち、訪問済みの要素と一致します。

プライバシー上の理由から、適用できるスタイルに制約があります。
- 利用できるCSSプロパティは以下の通り
  - `color`
  - `background-color`
  - `border-color`(ショートハンドではないものも含む)
  - `column-rule-color`
  - `text-decoration-color`
  - `text-emphasis-color`
- 利用できるSVG属性は`fill`と`stroke`
- 利用できるスタイルでもアルファチャンネルは無視される
- `<link>`要素は`:visited`に一致することはない
- `window.getComputedStyle`[^2]メソッドは`:visited`状態ではない色の値を返す

[^2]: アクティブなスタイルシートに含まれるすべてのCSSプロパティの値を含むオブジェクトを返すメソッド

### `:target`

URLの#で記述するフラグメントに一致するidを持つ要素と一致します。

## テキスト関連

### `:lang()`

指定された言語に一致する要素を選択します。
つまり、言語別にスタイルを適用することが出来ます。

## 画面の状態

### `:fullscreen`

全画面モードの要素に一致します。複数あればすべて選択します。

### `:picture-in-picture`

ピクチャインピクチャモードの要素に一致します。

## 印刷

すべて`@page`アットルールで使用されます。

### `:first`

印刷文書の最初のページと一致します。

文書のマージン、改ページするとき前ページに印刷する最低行数を設定する`orphans`、改ページするとき次ページに印刷する最低行数を設定する`windows`、ページ区切りを変更することが出来ます。
また、マージンは`px`や`in`などの絶対的な長さの単位のみ使用できます。

### `:left`

印刷文書の左側のページと一致します。

ページの`margin`、`padding`、`border`、`background`の各プロパティを変更することができます。

### `:right`

印刷文書の右側のページと一致します。


# 疑似要素

疑似要素は、その要素の**特定の部分**に対してスタイルを付与することができるものです。
CSSで記述する際は、`::before`のように、コロンを2つつけます。

```css:構文
selector::pseudo-element {
  property: value;
}
```

## `::after`

選択した要素の子要素の**最後**に疑似要素を生成します。
`<img>`、`<br>`などの置換要素[^3]には適用されません。

[^3]: 文書スタイルに影響されない要素。要素ボックス内のコンテンツ位置を設定できるプロパティのみが影響を与えることができる

`content`プロパティを用いて、要素の装飾によく使われます。
また、`content`は空にして、`background`に画像を設定したりして装飾することも多いです。

:::note
疑似クラスと疑似要素を見分けやすくするために、CSS3でコロン2つの表記法`::after`が導入されましたが、CSS2の`:after`も使用することが出来ます。
:::

## `::before`

選択した要素の子要素の**最初**に疑似要素を生成します。
その他`::after`と同様です。

## `::backdrop`

::: note warn
~~Experimentalではありますが、この要素自体はほぼ全てのブラウザで使用可能になったので記載します。~~
Experimentalが外されました。
:::

何らかの要素が全画面モードで表示されたときにそのすぐ下のレイヤーに表示されるViewportサイズのボックス要素です。
これはFullscreen APIを使用して全画面モードで配置される要素やおよび[`<dialog>`](https://developer.mozilla.org/ja/docs/Web/HTML/Element/dialog)（[MDN Web Docs](https://developer.mozilla.org/ja/docs/Web/HTML/Element/dialog)参照）要素を表示するときに用いられます。

全画面モードで複数の要素が配置されたときは、`::backdrop`は最上位要素のすぐ下に描画されます。

`::backdrop`はどの要素にも継承せず、どの要素からも継承せず、プロパティの制限もありません。

## `::cue`

選択された要素内のWebVTTキューを指定します。VTTトラック付きのメディアで、キャプションなどをスタイル付けすることができます。WebVTTについては[こちら](https://developer.mozilla.org/ja/docs/Web/API/WebVTT_API)を参照してください。

[使用可能なプロパティ](https://developer.mozilla.org/ja/docs/Web/CSS/::cue#%E5%88%A9%E7%94%A8%E5%8F%AF%E8%83%BD%E3%81%AA%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3)

## `::first-letter`

ブロックレベル要素の最初の行の一番最初の文字を指定します。ただし、最初の文字より前に他のコンテンツがない場合に限ります。

単純に**要素の最初の文字を識別できるとは限らない**ため注意が必要です。
例えば、`::before`と`content`プロパティで、要素の先頭にテキストが挿入された場合、この挿入されたコンテンツの最初の文字に一致します。

[利用可能なプロパティ](https://developer.mozilla.org/ja/docs/Web/CSS/::cue#%E5%88%A9%E7%94%A8%E5%8F%AF%E8%83%BD%E3%81%AA%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3)

## `::first-line`

ブロックレベル要素の最初の行を指定します。
なお、選択できる最初の行の長さは、要素・文書の幅、文字列のフォントの大きさなど、**様々な要因に左右**されます。

[利用可能なプロパティ](https://developer.mozilla.org/ja/docs/Web/CSS/::first-line#%E5%88%A9%E7%94%A8%E5%8F%AF%E8%83%BD%E3%81%AA%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3)

## `::file-selector-button`

`type="file"`の`input`要素を指定します。
「ファイルを選択」のようなボタンにスタイルを適用することができます。

古いWebKit/Blink互換のブラウザは`-webkit` prefixが必要です。（`::-webkit-file-upload-button`）

## `::selection`

ユーザーがテキストをマウスでクリックやドラッグすることで選択した文書の一部にスタイルを適用します。

利用可能なプロパティ
- color
- background-color
- text-decoration
- text-shadow
- SVG関連 (stroke-color, fill-color, stroke-width)

## `::slotted()`

HTMLテンプレート内にあるスロットに配置された任意の要素を指定します。
[テンプレートとスロットについて](https://developer.mozilla.org/ja/docs/Web/Web_Components/Using_templates_and_slots)

これはshadow DOM内に配置されたCSSの中で使われた時のみ機能します。

```css
/* スロット内に配置された任意の要素を選択 */
::slotted(*) {
  font-weight: bold;
}

/* スロット内に配置された<span>要素を選択 */
::slotted(span) {
  font-weight: bold;
}
```

# まとめ

以上、疑似要素と疑似クラスについてまとめました。

知らなかった要素もちらほらあり、自分もぜひ活用していきたいと思います。

# 参考

https://developer.mozilla.org/ja/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements