はじめに
この記事ではWeb Componentsを利用する準備のためにShadow DOMを紹介する記事です。Web Componentsとは他のコードから独立して利用できる再利用可能なコンポーネント(要素)を指します。
Web Componentを利用するために必要な他の要素としてCustom Elementについての記事、HTML Templateについての記事も書きましたので併せてご覧ください。また、Web Components自体の実装はこちらの記事で行いました。
Shadow DOMとは
Shadow DOMはカプセル化を行ってくれる機能で、他のコードから独立したDOMを作ることができます。隠された(Shadow) DOMというわけです。Shadow DOMとなった要素は外部からの変更は疎か取得を行うこともできません。Shadow DOMに対して通常のDOMはLight DOMと呼ばれています。
Shadow DOMを作る
特定の要素に対してattachShadowを呼び出すことでその要素をShadow DOM化させることができます。要素自体をShadow Host、要素にぶら下がる構造をShadow Treeと言います。
attachShadowは引数にオブジェクトを持ち、modeとdelegatesFocusをキーに持つことができます。modeは'open'と'closed'の2種類のバリューを取ります。'open'は外部のJavaScriptからの呼び出しが可能に、'closed'は不可能にする設定です。delegatesFocusはフォーカスに関する設定で真偽値をバリューに取ります。trueにした場合はフォーカス不可のShadow DOM内部の要素がクリックされた場合Shadow DOMのフォーカス可能な最初の要素がフォーカスされるようになります。
attachShadowの返り値はShadowRootオブジェクトです。このオブジェクトはDocumentFragmentを継承しているので通常のNodeと同じように扱うことができます。もちろん専用のプロパティやメソッドも実装されていてmodeやgetAnimationなどを使えます。
セキュリティの観点などからattachShadowは全ての要素に対して与えることはできないことに注意して下さい。与えられる要素はarticle, aside, blockquote, body, div, footer, h1, h2, h3, h4, h5, h6, header, main nav, p, section, spanです。
具体例を用いて挙動を解説します。
See the Pen ShadowDOM5 by KokiSakano (@kokisakano) on CodePen.
この例ではShadow Hostをshadowをidとするdiv要素に、追加したpがShadow Treeとなります。次に以下のコードを用いてDOM内のpの個数と、shadow持つdivから再度ShadowRootを取得してみます。
console.log(document.getElementsByTagName('p').length);
console.log(document.getElementById('shadow').shadowRoot.innerHTML);
pが存在しているのにDOM内のpの個数は0と出力されてしまいました。これはShadow DOMがもつ他のコードと独立させる特徴のためです。この取得方法ではLight DOMからしか取得できないというわけです。外部からのアクセスではこのようにShadow DOMの中身はないものとして扱われます。
ShadowRootの取得はattachShadowの返り値の他にShadow HostのshadowRootプロパティにアクセスすることで行えます。そのため二行目の結果は"<p>I live in shadow DOM</p>"となります。attachShadowの引数に渡したobjectのmodeをclosedとした場合はnullが出力されます。closedにした場合はattachShadow以外でshadowRootを取得できないので大切に持っておく必要があります。
Shadow DOMとCSS
Shadow DOMはLight DOMのCSSの影響を受けないです。同様にShadow DOM内部のCSSはLight DOMに影響はありません。
Light DOMでpのcolorをredに設定した場合でもShadow DOMのpのcolorは変化がないままです(importantをつけているのは効果がないことを強調するためです)。
See the Pen ShadowDOM1 by KokiSakano (@kokisakano) on CodePen.
同様にShadow DOM内でpのcolorをredに設定した場合でもLight DOMのpのcolorは変化がないままです。
See the Pen ShadowDOM2 by KokiSakano (@kokisakano) on CodePen.
二つの例でShadow DOM内外でcssが分離されていることを確認できました。しかし、実は定義自体は分離されているものの継承は分離されていません。
See the Pen ShadowDOM2 by KokiSakano (@kokisakano) on CodePen.
上記のようにcolorなどのstyleはshadowDOMであっても親から継承されることに注意する必要があります。継承を避けたい場合はShadow DOM内の要素に明示的にstyleを当てるようにします。
See the Pen ShadowDOM4 by KokiSakano (@kokisakano) on CodePen.
まとめ
Web Componentsを利用するために必要なShadow DOMを学びました。この機能を用いて他のコードから分離したカプセル化した要素を作成することができるようになります。