uga.dev - A Front-end Engineer's shed

現在のテーマは「ライトモード」です。

CSSでShadow rootの有無を検知したい。

公開日:

  • #CSS
  • #Web標準

あるいは、要素がShadow hostかどうかを知りたい。そんなニーズはありませんか?筆者の場合は、まさにそれでした。

現状、Shadow DOMに関連するCSSセレクタは以下の通りです[1]

セレクタ

Shadow DOMのどこで使われるか

:host

内側

:host()

内側

:host-context()

内側

:has-slotted[2]

内側

::slotted()

内側

::part()

外側

そのほとんどはShadow DOMの内側で使われるものとなっています。

唯一、::part()は外側から一部の要素にマッチさせられる擬似要素セレクタですが、結局どのセレクタを用いても特定の要素がShadow hostかどうかは判定できません[3]

要素がShadow hostかどうか知りたいときの例

HTMLのサンプルコード
<li>
  <div id="widget">
    <script src="..."></script>
    <script src="..."></script>
  </div>
</li>

上記のようなマークアップで、#widget内部にサードパーティウィジェットが埋め込まれるとします。

この時、#widget内部のDOM構造が変化するならば、次のようにスタイリングすることでハンドリングできそうです。

CSSのサンプルコード
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目線のユースケースが議論されていなさそうだったため、今回の件をコメントに残しておきました

エッジケースではあると思いますが、議論の助けになれば嬉しいです。

余談

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年後が楽しみです。

脚注