10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CSSAdvent Calendar 2024

Day 17

CSSの擬似クラスとJavaScript対応表

Posted at

様々な擬似クラスの登場により、それぞれの用途に応じて柔軟に対象を絞り込んでスタイルを適用することが容易になりました。
これまでJavaScriptで実装していた機能の多くが、CSSでも実現可能になった部分も増えています。

しかし、依然としてJavaScriptで操作した方が見通しが良かったり、CSSだけではどうしても対応できないケースもあるので、それらについてまとめてみます。

ベースとなるHTML
<div class="container">
    <ul>
        <li class="item">A (li.item)</li>
        <li class="item">B (li.item)</li>
        <li class="item">C (li.item)</li>
        <li>D (li)</li>
    </ul>
    <p>E (p)</p>
    <p class="item">F (p.item)</p>
    <ul>
        <li class="item">G (li.item)</li>
        <li>H (li)</li>
        <li class="item">I (li.item)</li>
        <li class="item">J (li.item)</li>
    </ul>
    <ul>
        <li>K (li)</li>
        <li>L (li)</li>
        <li>M (li)</li>
    </ul>
    <ul>
        <li class="item">N (li.item)</li>
        <li class="item">O (li.item)</li>
    </ul>
    <p>P (p)</p>
    <p class="item">Q (p.item)</p>
    <ul>
        <li>R (li)</li>
        <li class="item">S (li.item)</li>
        <li>T (li)</li>
    </ul>
</div>
ベースとなるCSS
.container {
    border: 1px solid black;
    font-family: monospace;
    width: 200px;
}

See the Pen Untitled by Yoruaki (@yoruaki) on CodePen.

コードの簡潔さと可読性を考慮して、便宜上.containerで全体を囲んでいます。

そのため、.containerの兄弟要素にはスタイルが適用されないなどの制約がありますが、本記事では擬似クラスの適用範囲や動作確認を視覚的に分かりやすくすることを目的としています。

全ての.itemにスタイルを適用したい

CSS
.container .item {
    background-color: red;
}
JavaScript
document.querySelectorAll('.item').forEach(function(element) {
    element.style.backgroundColor = 'red';
});

CSSで対応できるものは、CSSに任せましょう。

最初の.itemにスタイルを適用したい

CSS
.container .item:first-of-type {
    background-color: red;
}

このセレクタは、各親要素の中で最初に出現する要素かつ.itemだった場合に適用されるため、複数の親要素がある場合や、最初に.itemが来ない場合には意図した結果にならないことがあります。
つまり「最初の要素ではないかつ最初に出現する.item」には適用されません。

今回のケースだとA、G、Nに適用され、FとSには適用されません。

.item:first-of-typeの例

最初の要素に.itemが出現することが確定しているなら、そのままCSSで指定しましょう。

各要素内で最初に出現する.itemにスタイルを適用したい

CSSでは「最初の要素」は取得できても、「最初に出現する特定の要素」は取得できません。
そのため、この場合はJavaScriptでの実装が必要です。

JavaScript
document.querySelectorAll('.container > *').forEach(parent => {
    const firstItem = parent.querySelector('.item');
    if (firstItem) {
        firstItem.style.backgroundColor = 'red';
    }
    if (parent.classList.contains('item')) {
        parent.style.backgroundColor = 'red';
    }
});

各要素内で最初に出現する.itemにスタイルを適用した例

ドキュメント全体で最初に出現する.itemだけにスタイルを適用したい

あまりないとは思いますが、ドキュメント全体で最初の.itemだけにスタイルを適用したい場合は、JavaScriptで実装するしかありません。

JavaScript
const firstItem = document.querySelector('.item');
if (firstItem) {
    firstItem.style.backgroundColor = 'red';
}

ドキュメント全体で最初に出現する.itemだけにスタイルを適用した例

最後の.itemにスタイルを適用したい

CSS
.container .item:last-of-type {
    background-color: red;
}

こちらも.item:first-of-typeセレクタと同じく、「最後の要素ではないかつ最後に出現する.item」には適用されません。

.item:last-of-typeの例

このケースも、本来の意図はC、Sにも適用させたいものと想像できます。
もしこのままでOKなら、そのままCSSで指定しましょう。

各要素内で最後に出現する.itemにスタイルを適用したい

こちらも同じく、CSSでは「最後の要素」は取得できても、「最後に出現する特定の要素」は取得できません。
そのため、この場合もJavaScriptが必要です。

JavaScript
document.querySelectorAll('.container > *').forEach(parent => {
    const childItems = parent.querySelectorAll('.item');
    if (childItems.length > 0) {
        const lastItem = childItems[childItems.length - 1];
        lastItem.style.backgroundColor = 'red';
    }
});

const parentItems = document.querySelectorAll('.container > .item');
const lastParentItem = parentItems[parentItems.length - 1];
if (lastParentItem) {
    lastParentItem.style.backgroundColor = 'red';
}

各要素内で最後に出現する.itemにスタイルを適用した例

ドキュメント全体で最後に出現する.itemだけにスタイルを適用したい

こちらもあまりないとは思いますが、ドキュメント全体で最後の.itemだけにスタイルを適用したい場合も、JavaScriptで実装するしかありません。

JavaScript
const items = document.querySelectorAll('.item');
if (items.length > 0) {
    const lastItem = items[items.length - 1];
    lastItem.style.backgroundColor = 'red';
}

ドキュメント全体で最後に出現する.itemだけにスタイルを適用した例

特定の要素が.itemだったらスタイルを適用したい

CSSで指定しましょう。

CSS
.container p.item {
    background-color: red;
}

.itemを持っている親要素にスタイルを適用したい

CSSで指定しましょう。

CSS
.container :has(.item) {
    background-color: red;
}

:has(.item)の例

.itemを持っていない親要素にスタイルを適用したい

CSS
.container > *:not(:has(.item)) {
    background-color: red;
}

しかしこの場合、そもそも子要素を持っていない要素にもスタイルが適用されてしまいます。

*:not(:has(.item))の例

これが意図した通りならそのままCSSで指定しましょう。

CSSには、その要素が子要素を持っているかどうかを判定する機能はありません。その場合もJavaScriptで実装しましょう。

JavaScript
document.querySelectorAll('.container > *').forEach(element => {
    if (element.children.length > 0 && !element.querySelector('.item')) {
        element.style.backgroundColor = 'red';
    }
});

子要素がありかつ.itemを持っている要素にスタイルを適用した例

兄弟要素がない要素にスタイルを適用したい

以下のHTMLで、A、Dに適用したい場合ですね。

HTML
<div class="container">
    <ul>
        <li>A</li>
    </ul>
    <ul>
        <li>B</li>
        <li>C</li>
    </ul>
    <ul>
        <li>D</li>
    </ul>
    <p>E</p>
</div>

CSSで指定しましょう。

CSS
.container :only-child {
	  background-color: red;
}

同じ型で兄弟要素がない要素にスタイルを適用したい

上記の例だと、A、D、Eに適用したい場合ですね。
こちらもCSSで指定しましょう。

CSS
.container :only-of-type {
     background-color: red;
}

only-childでEに適用されなかった理由は、p要素はul要素と型違いの兄弟だからです。
逆にonly-of-typeは、兄弟だけど型が違うからonlyだったのです。

兄弟要素の中で自分だけ.itemを持っている要素にスタイルを適用したい

上記の例だと、Sに適用させたいパターンですね。
この場合、Sは.item:only-childでも.item:only-of-typeでも指定できません。

ここはJavaScriptが適しています。

JavaScript
document.querySelectorAll('.item').forEach(item => {
    const siblings = Array.from(item.parentElement.children);
    const itemSiblings = siblings.filter(sibling => sibling.classList.contains('item'));
    if (itemSiblings.length === 1) {
        item.style.backgroundColor = 'red';
    }
});

中身が空の.itemにスタイルを適用したい

CSSで指定しましょう。

CSS
.container .item:empty {
    background-color: red;
}

各セクションのまとめ

ケース CSSで対応可能 JavaScriptが必要 補足説明
全ての.itemにスタイルを適用したい CSSで十分
最初の.itemにスタイルを適用したい :first-of-typeを使用
各要素内で最初に出現する.itemにスタイルを適用したい CSSでは難しい
ドキュメント全体で最初に出現する.itemだけにスタイルを適用したい CSSでは難しい
最後の.itemにスタイルを適用したい :last-of-typeを使用
各要素内で最後に出現する.itemにスタイルを適用したい CSSでは難しい
ドキュメント全体で最後に出現する.itemだけにスタイルを適用したい CSSでは難しい
特定の要素が.itemだったらスタイルを適用したい セレクタで直接指定可能
.itemを持っている親要素にスタイルを適用したい :has()を使用
.itemを持っていない親要素にスタイルを適用したい :not():has()を使用
※子要素の有無も判定するならJSを使用
兄弟要素がない要素にスタイルを適用したい :only-childを使用
同じ型で兄弟要素がない要素にスタイルを適用したい :only-of-typeを使用
兄弟要素の中で自分だけ.itemを持っている要素にスタイルを適用したい CSSでは難しい
中身が空の.itemにスタイルを適用したい :emptyを使用

まとめ

以上、CSSの擬似クラスによるスタイル指定と、代替となるJavaScriptの使用例のまとめでした。

今回はHTMLが動的に変更され、こちらがコントロールできない場合を想定して書きましたが、通常、それ専用のクラスを適用したりすることで多くのケースをカバーできるかと思います。

しかしながら、中にはCSSだけではどうしても対応が難しい問題もあるかと思いますので、柔軟にCSSとJavaScriptを併用することで、より効果的なスタイリングをしていきましょう。

この記事が少しでもスタイル指定の参考になれば幸いです。

10
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?