Shadow DOM はカプセル化に役立ちます。これにより、コンポーネントは独自の “shadow(隠れた存在の)” DOM ツリーを持つことができます。これは、メインの document から誤ってアクセスされることなく、ローカルのスタイルルールなどを持つことができます。
組み込みの shadow DOM
複雑なブラウザ制御がどのようにして作成され、スタイルされているか考えたことはあるでしょうか?
例えば <input type="range"> です:
ブラウザはそれらを描画するために内部的に DOM/CSS を使います。そのDOM構造は通常隠されていますが、開発者ツールで見ることができます。E.g. Chrome では開発者ツールで “Show user agent shadow DOM” オプションを有効にする必要があります。
<input type="range"> は次のように見えます:
#shadow-root の下に見えるのが “shadow DOM” と呼ばれているものです。
通常の JavaScript 呼び出しやセレクタでは組み込みの shadow DOM 要素を取得することはできません。これらは通常の子ではなく、強力にカプセル化された技術です。
上の例では、便利な属性 pseudo が確認できます。これは標準ではありませんが、歴史的な理由で存在しています。これは、次のように CSS でスタイルのサブ要素として使うことができます。:
<style>
/* スライダーを赤にします */
input::-webkit-slider-runnable-track {
background: red;
}
</style>
<input type="range">
繰り返しますが、pseudo は非標準の属性です。時系列的には、ブラウザは最初、コントロールを実装するために内部的なDOM構造を試み始めました。その後、shadow DOMは標準化され、開発者も同様のことができるようになりました。
今後は、DOM specや他の関連仕様により記述されてる最新の shadow DOM 標準を使用します。
Shadow tree
DOM 要素には2種類の DOM のサブツリーがあります。:
- Light tree – HTMLの子からなる、通常の DOM サブツリーです。これまでのチャプターで見てきたすべてのサブツリーは “light” でした。
- Shadow tree – 隠された DOM サブツリーです。HTML には反映されず、詮索好きな目からは隠されています。
要素が両方を含んでいる場合、ブラウザは shadow tree のみをレンダリングします。しかし、shadow ツリーと light ツリーを組み合わせることもできます。詳細に関しては、チャプター Shadow DOM スロット, コンポジション で説明します。
Shadow tree は、カスタム要素が中身をコンポーネントの内部に隠したり、コンポーネントローカルなスタイルを適用するために使うことができます。
例えば、この <show-hello> 要素は自身の内部 DOM を shadow tree に隠します。:
<script>
customElements.define('show-hello', class extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({mode: 'open'});
shadow.innerHTML = `<p>
Hello, ${this.getAttribute('name')}
</p>`;
}
});
</script>
<show-hello name="John"></show-hello>
これは、結果として得られる DOM が Chrome 開発者ツールでどのように見えるか、です。すべてのコンテンツは “#shadow-root” の下にあります。:
最初に、 elem.attachShadow({mode: …}) を呼び出すことで shadow tree が作られます。
2つ制限があります。
- 要素毎に作成できる shadow root は1つだけです。
elemはカスタム要素、あるいは次のいずれかでなければなりません。: “article”, “aside”, “blockquote”, “body”, “div”, “footer”, “h1…h6”, “header”, “main” “nav”, “p”, “section”, or “span”。<img>のような他の要素は shadow tree を持つことはできません。
mode オプションはカプセル化のレベルを設定します。次のいずれかの値である必要があります。:
-
"open"– shadow root はelem.shadowRootとして利用可能です。