Shadow DOMはカプセル化のために使用されます。コンポーネントが、メインドキュメントから誤ってアクセスされることのない、独自の「シャドウ」DOMツリーを持つことを可能にし、ローカルスタイルルールなどを適用できます。
組み込みのShadow DOM
複雑なブラウザコントロールがどのように作成され、スタイル設定されているか考えたことはありますか?
例えば<input type="range">
のようなものです。
ブラウザはそれらを描画するために内部的にDOM/CSSを使用します。そのDOM構造は通常私たちからは隠されていますが、開発者ツールで見ることができます。例えばChromeでは、開発者ツールで「Show user agent shadow DOM」オプションを有効にする必要があります。
すると<input type="range">
は次のようになります。

#shadow-root
の下に見えるものが「シャドウDOM」と呼ばれるものです。
組み込みのシャドウDOM要素は、通常のJavaScript呼び出しやセレクターでは取得できません。これらは通常の子供ではなく、強力なカプセル化技術です。
上記の例では、便利な属性pseudo
が見えます。これは非標準で、歴史的な理由で存在します。これを使ってCSSでサブ要素をスタイル設定することができます。
<style>
/* make the slider track red */
input::-webkit-slider-runnable-track {
background: red;
}
</style>
<input type="range">
もう一度言いますが、pseudo
は非標準の属性です。時系列的に、ブラウザは最初にコントロールを実装するために内部DOM構造の実験を開始し、その後、時間が経ってから、私たち開発者が同様のことをできるように、シャドウDOMが標準化されました。
この後、DOM仕様やその他の関連仕様でカバーされている最新のシャドウDOM標準を使用します。
シャドウツリー
DOM要素は2種類のDOMサブツリーを持つことができます。
- ライトツリー – HTMLの子からなる通常のDOMサブツリー。これまでの章で見てきたすべてのサブツリーは「ライト」でした。
- シャドウツリー – HTMLには反映されず、詮索好きな目から隠された、非表示のDOMサブツリー。
もし要素が両方を持つ場合、ブラウザはシャドウツリーのみをレンダリングします。しかし、シャドウツリーとライトツリーの間で一種の構成を設定することもできます。詳細については、この章の後半のShadow DOMスロット、構成で説明します。
シャドウツリーは、カスタム要素でコンポーネントの内部を隠し、コンポーネントローカルのスタイルを適用するために使用できます。
例えば、この<show-hello>
要素は、シャドウツリーで内部DOMを隠します。
<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>
これがChrome開発者ツールでのDOMの最終的な表示方法です。すべてのコンテンツは「#shadow-root」の下にあります。

まず、elem.attachShadow({mode: …})
の呼び出しによってシャドウツリーが作成されます。
2つの制限事項があります。
- 要素ごとに作成できるシャドウルートは1つだけです。
elem
は、カスタム要素であるか、「article」、「aside」、「blockquote」、「body」、「div」、「footer」、「h1…h6」、「header」、「main」、「nav」、「p」、「section」、または「span」のいずれかである必要があります。<img>
のような他の要素は、シャドウツリーをホストできません。
mode
オプションはカプセル化レベルを設定します。次の2つの値のいずれかを持つ必要があります。
-
"open"
– シャドウルートはelem.shadowRoot
として利用可能です。どのコードでも
elem
のシャドウツリーにアクセスできます。 -
"closed"
–elem.shadowRoot
は常にnull
です。attachShadow
によって返された参照によってのみシャドウDOMにアクセスできます(そしておそらくクラス内部に隠されています)。<input type="range">
のようなブラウザネイティブのシャドウツリーはクローズされています。それらにアクセスする方法はありません。
attachShadow
によって返されるシャドウルートは要素のようなものです。innerHTML
またはappend
のようなDOMメソッドを使用して、要素をポピュレートすることができます。
シャドウルートを持つ要素は「シャドウツリーホスト」と呼ばれ、シャドウルートのhost
プロパティとして利用可能です。
// assuming {mode: "open"}, otherwise elem.shadowRoot is null
alert(elem.shadowRoot.host === elem); // true
カプセル化
シャドウDOMはメインドキュメントから強く区切られています。
- シャドウDOM要素は、ライトDOMからの
querySelector
には見えません。特に、シャドウDOM要素は、ライトDOMの要素と競合するIDを持つ可能性があります。それらはシャドウツリー内でのみ一意である必要があります。 - シャドウDOMは独自のスタイルシートを持っています。外部DOMからのスタイルルールは適用されません。
例:
<style>
/* document style won't apply to the shadow tree inside #elem (1) */
p { color: red; }
</style>
<div id="elem"></div>
<script>
elem.attachShadow({mode: 'open'});
// shadow tree has its own style (2)
elem.shadowRoot.innerHTML = `
<style> p { font-weight: bold; } </style>
<p>Hello, John!</p>
`;
// <p> is only visible from queries inside the shadow tree (3)
alert(document.querySelectorAll('p').length); // 0
alert(elem.shadowRoot.querySelectorAll('p').length); // 1
</script>
- ドキュメントのスタイルは、シャドウツリーには影響しません。
- …ただし、内部からのスタイルは機能します。
- シャドウツリー内の要素を取得するには、ツリー内部からクエリする必要があります。
参考文献
- DOM: https://dom.spec.whatwg.org/#shadow-trees
- 互換性: https://caniuse.dokyumento.jp/#feat=shadowdomv1
- シャドウDOMは他の多くの仕様で言及されています。例えば、DOM Parsingはシャドウルートが
innerHTML
を持つことを規定しています。
まとめ
シャドウDOMは、コンポーネントローカルのDOMを作成する方法です。
shadowRoot = elem.attachShadow({mode: open|closed})
–elem
のシャドウDOMを作成します。mode="open"
の場合、elem.shadowRoot
プロパティとしてアクセスできます。innerHTML
またはその他のDOMメソッドを使用してshadowRoot
をポピュレートすることができます。
シャドウDOM要素
- 独自のID空間を持ち、
querySelector
のようなメインドキュメントからのJavaScriptセレクターには見えず、- メインドキュメントからではなく、シャドウツリーからのスタイルのみを使用します。
シャドウDOMが存在する場合、ブラウザは、いわゆる「ライトDOM」(通常の子供)の代わりにレンダリングします。この章のShadow DOMスロット、構成では、それらを構成する方法を見ていきます。
コメント
<code>
タグを、複数行の場合は<pre>
タグで囲み、10行以上の場合にはサンドボックス(plnkr、jsbin、codepen…)を使用してください。