はじめに
この記事では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を学びました。この機能を用いて他のコードから分離したカプセル化した要素を作成することができるようになります。