シャドウドーム

標準への参照: www.w3.org/TR/2013/WD-shadow-dom-20130514



シャドウDOMとは正確には何ですか:

シャドウDOM (またはドキュメントのシャドウモデル)-DOMツリーでカプセル化を実装するドキュメントの一部。 これ(シャドウモデル)はドキュメントの一部であり、ページ内に直接埋め込まれます。

クロームでのシャドウDOMのデバッグを簡素化するために、Webインスペクターで表示を有効にすることができます(設定-一般-シャドウDOMの表示)。



シャドウDOMはドキュメントに埋め込まれ、相互に「独立して」(多かれ少なかれ独立して)動作する多くの部分の1つであるため、標準では実装されたカプセル化は機能と呼ばれます。 したがって、実装を設計する際に、ドキュメントツリーに機能的な境界を確立して、このような「独立した」フラグメントを何らかの方法で操作する必要がありました。 カプセル化の問題を解決するために、同じ抽象ツリー内に複数のDOMツリーを作成できるシャドウDOMという新しい抽象概念が導入され、それを説明するドキュメントが開発されました。





子ツリーは、ページ上のいくつかの要素内に配置されます。 メインドキュメントツリーとシャドウの間の機能境界は、シャドウ境界と呼ばれます。 シャドウツリーをホストする要素はシャドウホストと呼ばれ、シャドウツリーのルートはそれぞれシャドウルートと呼ばれます。







レンダリング中、シャドウツリーはシャドウホスト(要素)のコンテンツの代わりになります。



クロムでの実装例:



<div id="shadow-host"></div>
      
      







 var shadowHost = document.querySelector("#shadow-host"), shadowRoot = shadowHost.webkitCreateShadowRoot();
      
      











挿入点


挿入ポイントは、シャドウホストとシャドウツリーの子孫を構成するために使用されます。 挿入ポイントは、シャドウツリーでシャドウホストの子孫を見つけます。 シャドウツリーをレンダリングするとき、子孫はこの場所に投影されます。 シャドウホストのどの子孫を挿入ポイントに投影するかを決定するメカニズムは、配布と呼ばれます。



実装:



 <div id="shadow-host"> <span>Hi shadow DOM!</span> </div>
      
      







 var shadowHost = document.querySelector("#shadow-host"), shadowRoot = shadowHost.webkitCreateShadowRoot(), content = document.createElement("content"); content.select = "span"; //     shadow host shadowRoot.appendChild(content);
      
      











擬似要素::分散()


:: distributed(セレクター) -相対セレクターを引数として取る機能的な擬似要素。 これは、シャドウツリーの挿入ポイントと挿入ポイントに引き継がれる要素との関係を表します。



実装(クロムカナリアのみ):



 <html> <head> <script> function onLoad() { var shadowHost = document.querySelector("#shadow-host"), shadowRoot = shadowHost.webkitCreateShadowRoot(); shadowRoot.innerHTML = document.querySelector("template").innerHTML; } </script> </head> <body onload="onLoad()"> <div id="shadow-host"> <span>Hi shadow DOM!</span> </div> <template> <style> content::-webkit-distributed(span) { color: red !important; } </style> <content></content> </template> </body> </html>
      
      







1つのシャドウホストに複数のシャドウツリーを含めることができます-シャドウツリーは追加された順に表示されます。 このツリーのセットは、シャドウスタックと呼ばれます。 シャドウ挿入ポイントを使用して、古いシャドウツリーを別のシャドウツリーに転送することもできます。



 <html> <head> <script> function onLoad() { var shadowHost = document.querySelector("#shadow-host"), firstShadowRoot = shadowHost.webkitCreateShadowRoot(), secondShadowRoot = shadowHost.webkitCreateShadowRoot(); firstShadowRoot.innerHTML = document.querySelector("#template-1").innerHTML; secondShadowRoot.innerHTML = document.querySelector("#template-2").innerHTML; } </script> </head> <body onload="onLoad()"> <div id="shadow-host"> <span>Hi shadow DOM!</span> </div> <template id="template-1"> <div>root 1</div> </template> <template id="template-2"> <div>root 2</div> <shadow></shadow> </template> </body> </html>
      
      







再投影


再投影は、最初のシャドウツリーに既に挿入ポイントがあり、2番目のシャドウツリーにシャドウ挿入ポイントがあり、シャドウホストから取得されたコンテンツが最初に最初のシャドウツリーに投影され、次に2番目に投影される状況です。



 <html> <head> <script> function onLoad() { var shadowHost = document.querySelector("#shadow-host"), firstShadowRoot = shadowHost.webkitCreateShadowRoot(), secondShadowRoot = shadowHost.webkitCreateShadowRoot(); firstShadowRoot.innerHTML = document.querySelector("#template-1").innerHTML; secondShadowRoot.innerHTML = document.querySelector("#template-2").innerHTML; } </script> </head> <body onload="onLoad()"> <div id="shadow-host"> <span>Hi shadow DOM!</span> </div> <template id="template-1"> <div>root 1</div> <content select="span"></content> </template> <template id="template-2"> <div>root 2</div> <shadow></shadow> </template> </body> </html>
      
      







擬似要素(シャドウDOMのコンテキスト内)


標準の著者は次のように書いています

特定の状況では、シャドウツリーの作成者は、そのツリーの1つ以上の要素を、シャドウツリーのコンテンツに関する追加情報を提供する構造抽象として指定することができます。


特定の状況では、シャドウツリーの作成者は、シャドウツリーのコンテンツに関する追加情報を提供する構造的な抽象化として、シャドウツリーから1つ以上の要素を割り当てたい場合があります。



シャドウツリーの外側のCSSセレクターを使用して、その内部の要素にアクセスする機能として理解していること:

 <html> <head> <script> function onLoad() { var shadowHost = document.querySelector("#shadow-host"), shadowRoot = shadowHost.webkitCreateShadowRoot(); shadowRoot.innerHTML = document.querySelector("template").innerHTML; } </script> <style> div::x-thumb { width: 10px; height: 10px; background: black; } </style> </head> <body onload="onLoad()"> <div id="shadow-host"></div> <template> <div pseudo="x-thumb"></div> </template> </body> </html>
      
      







イベント


一部のイベントはシャドウ境界を通過しますが、一部は通過しません。 例外はミューテーションイベントです。シャドウツリーではまったく発生しないため、シャドウ境界を通過する必要があります。 イベントがシャドウ境界を通過すると、event.targetが変更され、カプセル化が維持されます。

興味深い例を次に示します。

 <html> <head> <script> function onLoad() { var shadowHost = document.querySelector("#shadow-host"), shadowRoot = shadowHost.webkitCreateShadowRoot(); shadowRoot.innerHTML = document.querySelector("template").innerHTML; shadowHost.addEventListener("mouseout", function(e) { console.log("mouse out", e.target); }); } </script> <style> #shadow-host { width: 100px; height: 100px; background: blue; } #outer-element { width: 100%; height: 20px; background: red; } </style> </head> <body onload="onLoad()"> <div id="shadow-host"> <div id="outer-element"></div> </div> <template> <div id="first-inner-element"></div> <div id="second-inner-element"></div> <content></content> <style> #first-inner-element { width: 100px; height: 20px; background: green; position: absolute; top: 140px; } #second-inner-element { width: 100px; height: 20px; background: black; margin-bottom: 40px; } </style> </template> </body> </html>
      
      







投影された要素のイベントは、シャドウホスト内にポップアップします。シャドウホスト内に直接存在するかのようです。 シャドウホストの外部に絶対に配置され移動されるsecond-inner-elementとは異なり、first-inner-elementイベントはシャドウホストにポップアップしません(event.targetが変更されています)。



スタイル


シャドウツリースタイルを操作するには、2つの方法があります。



shadowRoot.resetStyleInheritance (デフォルトではfalse)

シャドウツリーのスタイルの継承をリセットします(外側のスタイルはシャドウツリーに適用されません)。



shadowRoot.applyAuthorStyles (デフォルトではfalse)

作成者(メイン)ドキュメントのスタイルを適用します。



 <html> <head> <script> function onLoad() { var shadowHost = document.querySelector("#shadow-host"), firstShadowRoot = shadowHost.webkitCreateShadowRoot(); var secondShadowRoot = shadowHost.webkitCreateShadowRoot(); secondShadowRoot.resetStyleInheritance = true; secondShadowRoot.applyAuthorStyles = true; firstShadowRoot.innerHTML = document.querySelector("#template-1").innerHTML; secondShadowRoot.innerHTML = document.querySelector("#template-2").innerHTML; } </script> <style> * { font-style: italic; } </style> </head> <body onload="onLoad()"> <div id="shadow-host"></div> <template id="template-1"> <style> * { color: red; font-weight: bold; } </style> <div>root 1</div> </template> <template id="template-2"> <div>root 2</div> <shadow></shadow> </template> </body> </html>
      
      







まとめ



htmlの一部の「カプセル化」では不十分だったと言えます。 これにより、ページ上でさまざまな事前に準備されたウィジェットを作成およびテンプレート化する大きな機会が開かれます。 唯一の驚きは、ウィジェット内にJavaScriptコードがカプセル化されていないことですが、私にはかなり論理的に思えます。



All Articles