CSSの疑似クラス:has関数での実装例
2025年 5月15日 Posted 野々瀨(フロントエンドエンジニア)
CSSで子孫要素の状況や状態を探るには、JavaScriptを介す必要がありました。しかし、CSSのセレクターの一つである:has()
関数の登場により、JavaScriptを使用することなくCSSだけで実現できるようになりました。案件でのサポート状況からなかなか使用が限られていましたが、IEなど古いブラウザーの対応から開放され、ようやく使用できるようになったうちの一つです。
そんな:has()
関数について、一部ではありますが使用した例をご紹介しようと思います。
例:子孫要素が存在
:has()
関数に指定したセレクターに一致する子孫要素に対して、スタイルを適用します。
.sample:has(span) {
color: #d00;
}
<!-- 一致 -->
<div class="sample"><span>あいうえお</span></div>
<!-- 一致 -->
<div class="sample"><p><span>あいうえお</span></p></div>
<!-- 不一致 -->
<div class="sample">あいうえお</div>
引数を複数指定した場合、指定した引数のいずれかが一致(OR判定)すれば適用されます。
.sample:has(span, a) {
color: #d00;
}
<!-- 一致 -->
<div class="sample"><span>あいうえお</span></div>
<!-- 一致 -->
<div class="sample"><a>あいうえお</a></div>
複数の:has()
関数を指定しますと、それらに全て一致する場合のみ(AND判定)適用されます。
.sample:has(span):has(a) {
color: #d00;
}
<!-- 一致 -->
<div class="sample"><a><span>あいうえお</span></a></div>
<!-- 不一致 -->
<div class="sample"><span>あいうえお</span></div>
<!-- 不一致 -->
<div class="sample"><a>あいうえお</a></div>
例:子要素が存在
引数の先頭に>
(子結合子)を指定することで、子要素のみに限定することができます。
p:has(> span) {
color: #d00;
}
<!-- 一致 -->
<p><span>あいうえお</span></p>
<!-- 不一致 -->
<p><a><span>あいうえお</span></a></p>
例:次の子要素が存在
引数の先頭に+
(後続兄弟結合子)を指定することで、直後に隣り合う兄弟要素のみに限定することができます。
p:has(+ .sample) {
color: #d00;
}
<!-- 一致 -->
<p>あいうえお</p>
<div class="sample">かきくけこ</div>
<!-- 不一致 -->
<p>あいうえお</p>
<span>さしすせそ</span>
<div class="sample">かきくけこ</div>
例:子孫要素が存在しない(否定)
:not()
関数を使用しますと否定、つまり反対の意味になり引数で指定したセレクターを持っていないという表現にすることができます。
p:not(:has(span)) {
color: #d00;
}
<!-- 一致 -->
<p>あいうえお</p>
<!-- 不一致 -->
<p><span>あいうえお</span></p>
例:a要素がhoverしたとき
hoverしたとき、その要素の領域外にもスタイルを適用することができます。
.sample:has(a:hover) + img {
border: 1px solid #d00;
}
<p class="sample"><a href="#sample">あいうえお</a></p>
<img src="img/sample.png" width="120" height="120" alt="">
例:チェックボックスがチェック済みの状態のとき
チェックボックスにチェックされている状態のときにスタイルを適用することができます。inout[type="checkbox"]要素に対してlabel要素を囲ったり、inout[type="checkbox"]要素の後に続く兄弟要素にしたりする必要がなくなります。
.field:has([type="checkbox"]:checked) {
border: 1px solid #d00;
}
<div class="field">
<input type="checkbox" id="sample">
<label for="sample">あいうえお</label>
</div>
例:子要素の数の判定
子要素の数に応じてスタイルを適用することができます。
.sample:has(> :last-child:nth-child(3)) {
color: #d00;
}
<!-- 3つ:一致 -->
<div class="sample">
<p>あいうえお</p>
<p>かきくけこ</p>
<p>さしすせそ</p>
</div>
<!-- 2つ:不一致 -->
<div class="sample">
<p>あいうえお</p>
<p>かきくけこ</p>
</div>
<!-- 4つ:不一致 -->
<div class="sample">
<p>あいうえお</p>
<p>かきくけこ</p>
<p>さしすせそ</p>
<p>たちつてと</p>
</div>
例:カード型レイアウトの画像の有無
カード型レイアウトで画像がある場合とない場合で、レイアウトを調整することができます。
.cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.cards .card {
border: 1px solid #999;
padding: 15px;
}
.cards .card:has(img) {
display: grid;
grid-template: "image name" auto "image profile" 1fr / auto 1fr;
gap: 0 20px;
}
.cards .card:has(img) img {
grid-area: image;
}
.cards .card .name {
margin: 0;
}
.cards .card:has(img) .name {
grid-area: name;
}
.cards .card .profile {
margin: 0.5em 0 0;
}
.cards .card:has(img) .profile {
grid-area: profile;
}
<div class="cards">
<!-- 画像あり -->
<div class="card">
<img src="img/taro.png" width="120" height="120" alt="">
<p class="name">大塚 太郎</p>
<p class="profile">プロフィールテキスト</p>
</div>
<!-- 画像なし -->
<div class="card">
<p class="name">大塚 太郎</p>
<p class="profile">プロフィールテキスト</p>
</div>
</div>
ドキュメント
対応ブラウザー
最後に
:has()
関数以外も今後、兄弟要素や子孫要素の数の検知、条件分岐、ユーザー定義関数などさまざまな機能が予定されているようです。JavaScriptを介さずにCSSのみでスタイルを操作できるようになるのは、パフォーマンスがよくなったりそんなにテクニックを必要としなかったりしますので、非常にありがたいことです(ブラウザーのアプリケーション開発者は大変かと思いますが……)。
複雑になったりCSSがプログラミングチックになっていったりなど気になる点はありますが、便利になっていることも事実かと思いますので、こういったものはどんどん使っていきたいですね。