以前のCSSでは、要素の存在や非存在に基づいて特定の親要素や子要素にスタイルを設定することが困難でした。これまでは、存在する場合と存在しない場合の両方に対応するクラスを作成し、必要に応じてこれらのクラスを切り替えるという手間が発生していました。しかし、約1年前に導入された:has()は、この手間を省き、CSSの作成が以前よりもはるかにシンプルになりました。これにより、要素の存在に応じて親要素や子要素に簡単にスタイルを適用できるようになりました。
今回のブログでは、新たに導入された擬似セレクタ:has()の活用方法を解説いたします。
ブログ作成者紹介
氏名:P・P
所属:開発部
趣味:ボクシング、旅行
擬似クラス:has()とは?
:has()は、:hoverや:focusのような擬似クラスよりも、むしろ:isや:notといった擬似クラスと似た動作をします。:has()を使用するには、親要素に適用される:has()に子要素のセレクタ(またはセレクタのリスト)を渡す必要があります。これは言葉で説明するとわかりにくいので、例を示しましょう。
.header:has(.highlight) {
margin-bottom: 1rem;
}
このシンプルなセレクタは、.headerというクラスを持つ要素を選択し、その中に.highlightクラスを持つ要素が含まれている場合に、下部に特別なマージンを適用するよう指定しています。これにより、.headerの下部に1remのマージンを設定することができます。
<h1 class="header">
<!-- SELECTED -->
<span class="highlight"></span>
</h1>
<h1 class="header"></h1>
<!-- NOT SELECTED -->
これは通常のCSSではリスト内の最後の要素のみを選択できるため、実際には逆に思う方もいらっしゃるかと思いますが、:has()要素は他の要素内の要素の存在を確認しながら、実際の親要素を選択できるようにします。
高度な使用法
上記の例では:has()のもっとも単純な使い方を示しましたが、実際にはもっと多くのことができます。
複雑なセレクタの使用
:has()に単一のクラスだけでなく、任意のセレクタを渡すことができます。
.header:has(> .highlight strong#custom-color) {}
これははるかに複雑なセレクタでも正常に動作し、.header要素の直接の子要素に.highlightクラスが含まれ、その中にidがcustom-colorのstrong要素が含まれている.header要素を選択します。
親セレクタだけでなくセレクタ
これまでに話した例では、このセレクタを親セレクタとして使用していますが、実際にはそれ以上のことができます。
.header:has(.highlight) p {}
このセレクタは、.header要素内に.highlight要素が含まれている限り、.header要素内にある任意のpタグを選択します。:has()をセレクタの中間に使用することで、非常に複雑なセレクタを作成し、CSSでは難しいと思われていたことを実現することができます。
複数のセレクタを使用
:has()に複数のセレクタを組み合わせて、非常に複雑な条件を確認できます。
.header:has(.highlight, p) {}
.header:has(.highlight):has(p) {}
上記のコードは似ているように見えますが、実際には異なる条件を確認しています。最初のセレクタは、.header要素またはp要素のいずれかが含まれている.header要素を選択し、2番目のセレクタは.headerとp要素の両方が含まれている.header要素だけを選択します。
特異度
:has()を使用した特異度の計算方法は、通常の擬似クラスと比較してやや複雑です。
:has()と単一のセレクタしかない場合、特異度を計算するのは簡単です。:has()の特異度と外側のセレクタの特異度を単純に組み合わせるだけです。
.header:has(.highlight p) {}
このセレクタは、:has()の外側に1つのクラスがあり、:has()の内側に1つのクラスと1つの要素があるので、セレクタ全体の特異度は2つのクラスと1つの要素です。これは以下のコードと同じ特異度です。
h1.header.red {}
複数の:has()を取り扱う際には、各:has()ごとに特異性が計算され、全体のセレクタの特異度と組み合わされます。
.header:has(.highlight):has(p) {}
このセレクタは、最初の:has()が1つのクラスの特異度を持ち、2番目の:has疑似セレクタが1つの要素の特異度を持っているので、:has()の内側の特異度はすべての:has()の特異度と組み合わされ、外側のセレクタの特異度は1つのクラスであるため、このセレクタ全体の特異度は2つのクラスと1つの要素です。
正規表現との類似
:has()構文を、正規表現における先読みアサーションになぞらえることができる。この類似性は、正のルックアヘッド(?=)と負のルックアヘッド(?!)
肯定先読み
正規表現の領域では、abc(?=xyz)は文字列abcの直後に文字列xyzが続く場合のみ、xyzをマッチに含めずに選択します。:has()と等価なものは.abc:has(+ .xyz)と表現され、次の兄弟.xyzがある場合にのみ.abc要素が選択されることを意味します。:has(+ .xyz)は先読み操作のように動作し、.xyz要素を組み込まずに.abc要素が選択されることを確実にします。
否定先読み
正規表現における負の先読みシナリオ(abc(?!xyz)では、文字列abcの後にxyzが続かない場合にのみマッチします。CSSでは、類似の構文は.abc:has(+ :not(.xyz))で、次の要素が.xyzの場合、.abc要素は選択されないことを示します。
ブラウザサポート
現時点では、:has()はSafari 15.4とChrome/Edge 105で利用可能で、Firefoxではバージョン121でフラグが有効化されています
結論
CSSの進化に伴い、数年間待望していた親セレクタがついに実現しました。おおよそ1年前に導入された:has()は、従来の親セレクタを超えた機能を提供し、開発者にとっては画期的な進歩です。これにより、複雑なCSSスタイルをより効果的に構築でき、親子要素のスタイリングに関する一般的な課題に対処できるようになりました。
この新しいセレクタの導入は、開発プロセスにおいて柔軟性と効率性を向上させ、コードの可読性を高める一助となりました。例示されたように、シンプルな使用法から高度なセレクタの活用まで、:has()は多岐にわたる要件に対応できることが確認されています。
また、正規表現との興味深い類似性も浮かび上がり、開発者はこれまで以上に直感的かつ強力なスタイリングツールを手に入れたと言えます。
現在では、主要なブラウザで:has()が十分にサポートされており、開発者は安心してこの新しい機能を活用できます。CSSの未来に期待が高まる中、新たなセレクタの導入によってスタイリングの新たな次元が切り拓かれました。
おわりに
Vareal株式会社では、わたしたちと一緒に働いていただける方を大募集しています!
興味のある方はぜひ下記リンクをチェックしてみてください!
採用ページ
X(旧Twitter)では社内の雰囲気を発信しています、ぜひフォローしてみてください!
バレアルマン@広報 | ヒーロー【企業公式】