CSSの歴史を塗り替えた「:has()」という革命。もうJSでクラスを付け替える必要はない。
やあ。フロントエンドの現場で「あ、これ親要素にスタイル当てたいけど、JSでクラス操作するしかないか……」と、深夜にため息をついた経験はないだろうか?
僕たちは長年、CSSの「カスケード」という設計思想に縛られ、子要素の状態によって親の見た目を変えるために、泣く泣くDOMを操作するJavaScriptを書いてきた。だが、時代は変わった。`:has()` 擬似クラスの登場によって、僕たちの武器は劇的に進化したんだ。
今日は、この「CSS界の革命児」を、単なる仕様の解説ではなく、現場でどう使い倒すべきか、その勘所を叩き込んでいく。
—
`:has()` の本質:なぜ「親セレクタ」と呼ばれるのか
`:has()` は、一言で言えば「引数に指定したセレクタにマッチする要素を『内部に持っている』要素」を選択するセレクタだ。
例えば、`.card:has(img)` と書けば、「画像が含まれている `.card` だけを狙い撃ち」できる。今までなら「`img` が入っている時は `card–with-image` という修飾クラスを付ける」といったJSの制御が必要だった場面を、CSSだけで完結させられるようになった。
ブラウザの裏側で起きていること
ブラウザのレンダリングエンジンは、DOMツリーを上から下へ、あるいはその逆方向へ再帰的に走査する。`:has()` は「子要素の状態を親が感知する」という、従来のCSSの「上から下へ流れる」という一方通行のルールを覆すものだ。
パフォーマンスを懸念する声もあるが、主要ブラウザは高速化のために「`:has()` が使われている箇所だけを効率的に再評価する」最適化を行っている。とはいえ、あまりに広範囲で複雑な再帰構造に使うとブラウザの負荷になることは覚えておいてほしい。あくまで「賢く」使うのがプロだ。
—
現場で即戦力になる「賢い」活用パターン
実務でよく遭遇する「痒いところに手が届く」実装例を紹介しよう。コピペして、自分のプロジェクトのCSSに組み込んでみてくれ。
1. 画像の有無でレイアウトを劇的に変える
カードコンポーネントで、画像がある時は縦並び、ない時は横並び……なんて要件はよくあるよね。
.card {
display: flex;
gap: 16px;
padding: 16px;
border: 1px solid #ccc;
}
/ 画像が含まれている場合だけ、カードの構成を縦積み(カラム)にする /
.card:has(img) {
flex-direction: column;
}
/ 画像がない場合は、テキストが主役なので横幅を広げるなど /
.card:not(:has(img)) {
background-color: #f9f9f9;
}
2. バリデーションエラーを親に通知する
フォームバリデーションはJSの聖域と思われがちだが、CSSだけでもここまでできる。
/ 入力値が無効な状態のinputを内包している親フォームグループをハイライト /
.form-group:has(input:invalid) {
border-left: 4px solid #ff4d4f;
background-color: #fff2f0;
}
/ エラーメッセージを表示するラベルのスタイル /
.form-group:has(input:invalid) .error-label {
display: block;
color: #ff4d4f;
font-size: 0.8rem;
}
—
中級者以上が知っておくべき「落とし穴」
`:has()` は最強の武器だが、取り扱いには注意が必要だ。
- 過信は禁物: `:has()` を過剰に使いすぎると、CSSの依存関係が複雑になり、後からコードを読んだメンバーが「なぜここでスタイルが変わるんだ?」と混乱する。あくまで「DOM構造を汚さずに済む最小限の場所」に限定すること。
- セレクタの結合: `:has()` の中には、複雑なコンビネーター(子孫、隣接など)を詰め込める。だが、`div:has(.a .b .c)` のような深い階層指定は、レンダリングコストを増大させる可能性がある。可能な限りシンプルに保とう。
—
最後に:CSSは「文書の構造」を語るもの
`:has()` がもたらしたのは、単なるコードの短縮ではない。「JSで無理やりDOMを操作しなくても、状態(State)をCSSが直接表現できるようになった」というパラダイムシフトだ。
エンジニアとして大切なのは、JSを削り、CSSに任せられる部分はCSSに返してあげること。そうすることで、アプリケーションはより宣言的で、堅牢で、保守しやすいものになる。
もし明日、君のプロジェクトで「クラスの付け替え」のためにJSを書こうとしている場面に出くわしたら、一呼吸置いて考えてみてほしい。
「これ、`:has()` でいけないか?」と。
その問いかけができるようになった時、君はもう一段上のフロントエンド・エンジニアになっているはずだ。頑張ってくれ。応援しているぞ。

コメント