CSSでShadow rootの有無を検知したい。
公開日:
- #CSS
- #Web標準
あるいは、要素がShadow hostかどうかを知りたい。そんなニーズはありませんか?筆者の場合は、まさにそれでした。
現状、Shadow DOMに関連するCSSセレクタは以下の通りです[1]。
セレクタ | Shadow DOMのどこで使われるか |
|---|---|
| 内側 |
| 内側 |
| 内側 |
| 内側 |
| 内側 |
| 外側 |
そのほとんどはShadow DOMの内側で使われるものとなっています。
唯一、::part()は外側から一部の要素にマッチさせられる擬似要素セレクタですが、結局どのセレクタを用いても特定の要素がShadow hostかどうかは判定できません[3]。
要素がShadow hostかどうか知りたいときの例
<li>
<div id="widget">
<script src="..."></script>
<script src="..."></script>
</div>
</li>上記のようなマークアップで、#widget内部にサードパーティウィジェットが埋め込まれるとします。
この時、#widget内部のDOM構造が変化するならば、次のようにスタイリングすることでハンドリングできそうです。
li:has(#widget > script:first-child + script:last-child) {
display: none;
}一方で、#widget内部にShadow DOMが展開されるケースでは、Light DOM(通常のDOM)は変化しないため、現状のCSSだけだと検知できません。
そこで、要素がShadow hostかどうか(=Shadow DOMを持っているか)を判定するセレクタがあればいいなと思い、調べてみました。
既存のプロポーザル
これについての議論は、2018年にはCSS Working Group上で始まっていました。
2つのIssueがありますが、いずれも「Shadow rootを持つ要素に外側からマッチするセレクタが欲しい」というものです。CSS目線のユースケースが議論されていなさそうだったため、今回の件をコメントに残しておきました。
エッジケースではあると思いますが、議論の助けになれば嬉しいです。
余談
/* `::shadow-root`がhas-allowedな疑似要素として実装されたケースを想定 */
li:not(:has(#widget::shadow-root)) {
display: none;
}
li:not(:has(#widget:has-shadowroot)) {
display: none;
}筆者は直前まで::shadow-rootが好みでしたが、記事執筆時点の現在は:has-shadowrootを推したいと思います。
Shadow root自体を表す擬似要素セレクタが誕生したとしても、外側からはスタイリングできない(はず)です。それならば、Shadow hostがあるかどうかの状態を表すだけの擬似クラスセレクタのほうがシンプルだと思いました。:has-slotted擬似クラスとも一貫性を感じますね。
まもなくこの議論も10年。はたして、あと2年で話は進んでいくのでしょうか。2年後が楽しみです。
脚注
- [^1]:記事執筆時点。参考:CSS Shadow Module Level 1
- [^2]:記事執筆時点の
:has-slottedはEditor's Draft段階で、主要なブラウザの中ではFirefoxのみサポート。参考:CSS Shadow Module Level 1、Can I use - [^3]:
part属性が使われていれば:has(::part(hoge))ができそうだが、has-allowed pseudo-elementとして定義されていなければ:has()セレクタは引数に擬似要素セレクタを持てず、::part()擬似要素セレクタはその対象になっていない。参考:4.5. The Relational Pseudo-class: :has() - Selectors Level 4