JavaScript プログラミング講座

 

Shadow DOM について

 


■Shadow DOM について

 
Shadow DOM は、Web Components 関連の機能です。(実験段階)
 
http://www.w3.org/TR/shadow-dom/ (Working Draft)
 
複雑な入れ子構造になっている DOM ツリーを、あたかも一体化した1つの要素であるかのように、取り扱えるようになります。
 
■ <VIDEO> 要素の実例
 
例えば、<VIDEO> 要素のコントロール部分は、Shadow DOM で実装されています。
 
内部には、DOM ツリーが存在しますが、隠匿されています。
 
<VIDEO> 要素は、あたかも一体化した1つの要素のように取り扱うことができます。
 
 

■ Shadow Host について

 
任意の要素は、ShadowRoot オブジェクトを作成する事ができます。
 
ShadowRoot の作成については、こちらで解説しています。
 
もし作成した場合、その任意の要素は、Shadow Host となります。
 
Shadow Host に子孫が存在する場合、それらは非表示な状態へと変化します。
 

■ ShadowRoot について

 
ShadowRoot は、DOM オブジェクトの一種です。
 
DocumentFragment インターフェースを継承しています。
 
主な機能については、こちらで解説しています。
 

■ Shadow ツリーについて

 
■ Shadow ツリーを構築する
 
ShadowRoot の、ノードリストに、任意の DOM オブジェクトを登録します。
 
ShadowRoot を最上位とした、Shadow ツリーを構築する事ができます。
 
Shadow ツリーは、基本的に JavaScript から動的に構築します。
 
<TEMPLATE> 要素を使って、静的に準備しておく事もできます。
 
■ Shadow Host の描画について
 
Shadow Host の子孫は、ブラウザに表示されなくなります。
 
かわりに、ShadowRoot 内の Shadow ツリーが表示されるようになります。
 
■ Shadow ツリーのカプセル化について
 
Shadow ツリーは、隠匿された状態となります。
 
旧世代の API では、ShadowRoot 内にアクセスする事はできません。
 
セレクタを使ったアクセス方法については、こちらで解説しています。
 
DOM API を使ったアクセス方法については、こちらで解説しています。
 


 

ShadowRoot を作成する

 
 


■ ShadowRoot オブジェクトを作成する

 
attachShadow() メソッドを使用します。
 
Element.attachShadow( ShadowRootInit ) :ShadowRoot
引数(第01引数)ObjectShadowRootInit オブジェクトを指定。
戻り値 ShadowRootShadowRoot オブジェクトが得られる。
 
■ ShadowRootInit オブジェクトについて
 
新規にオブジェクトを作成し、以下のプロパティを追加します。
 
プロパティ名説明
modeString公開モードを設定する。
 
■作成例
 
ShadowRoot オブジェクトを作成し、Shadow ツリーを構築する

<html>
  <body>

    <div id="aaa" >
      <span style="color:#F00;">あいうえお</span>
    </div>

    <script type="text/javascript">
    <!--

	// ------------------------------------------------------------
	// "aaa" という ID 属性のエレメントを取得する
	// ------------------------------------------------------------
	var element = document.getElementById("aaa");

	// ------------------------------------------------------------
	// ShadowRootInit オブジェクトを作成する
	// ------------------------------------------------------------
	var shadow_root_init = new Object();

	// 公開モードを設定する
	shadow_root_init.mode = "closed";

	// ------------------------------------------------------------
	// ShadowRoot オブジェクトを作成する
	// ------------------------------------------------------------
	var shadow_root = element.attachShadow( shadow_root_init );

	// ------------------------------------------------------------
	// HTML 文字列をセットして、Shadow ツリーを構築する
	// ------------------------------------------------------------
	shadow_root.innerHTML = '<span style="color:#00F;">かきくけこ</span>';

    //-->
    </script>

  </body>
</html>
 

■公開モードについて(ShadowRootMode)

 
以下の公開モードが定義されています。
 
文字列説明
"open"StringShadow ツリーを第三者に公開する。
"closed"StringShadow ツリーを第三者に公開しない。(非公開モード)
 
■非公開モードについて
 
ShadowRoot オブジェクトを取得できるのは、attachShadow() メソッドを使用した当事者のみです。
 
非公開モードの場合

// ------------------------------------------------------------
// 要素を作成する
// ------------------------------------------------------------
var element = document.createElement("div");

// ------------------------------------------------------------
// ShadowRoot オブジェクトを非公開モードで作成する(作成した当事者のみが、戻り値から ShadowRoot オブジェクトを取得できる)
// ------------------------------------------------------------
var shadow_root0 = element.attachShadow( { mode:"closed" } );

// ------------------------------------------------------------
// ShadowRoot オブジェクトを取得する(第三者は、ShadowRoot オブジェクトを取得できない)
// ------------------------------------------------------------
var shadow_root1 = element.shadowRoot;

// ------------------------------------------------------------
// 出力テスト
// ------------------------------------------------------------
console.log(shadow_root0);
console.log(shadow_root1); // null
 

■ 作成済みの ShadowRoot オブジェクトを取得する

 
shadowRoot プロパティを使用します。
 
取得に失敗する場合は、null が得られます。(非公開モードなど)
 
作成済みの ShadowRoot オブジェクトを取得する

// ------------------------------------------------------------
// 要素を作成する
// ------------------------------------------------------------
var element = document.createElement("div");

// ------------------------------------------------------------
// ShadowRoot オブジェクトを作成する
// ------------------------------------------------------------
var shadow_root0 = element.attachShadow( { mode:"open" } );

// ------------------------------------------------------------
// ShadowRoot オブジェクトを取得する
// ------------------------------------------------------------
var shadow_root1 = element.shadowRoot;

// ------------------------------------------------------------
// 出力テスト
// ------------------------------------------------------------
console.log(shadow_root1);
console.log(shadow_root0 === shadow_root1); // true
 


 

ShadowRoot 内のコンテンツについて

 
 


■ ShadowRoot 内に Shadow ツリーを構築する

 
■ innerHTML プロパティを使用する
 
ShadowRoot は、innerHTML プロパティに対応しています。
 
「HTML 文字列」を指定して、Shadow ツリーを構築する事ができます。
 
HTML 文字列をセットして、Shadow ツリーを構築する

// ------------------------------------------------------------
// DIV 要素を作成する
// ------------------------------------------------------------
var element = document.createElement("div");

// ------------------------------------------------------------
// ShadowRoot オブジェクトを作成する
// ------------------------------------------------------------
var shadow_root = element.attachShadow( { mode:"open" } );

// ------------------------------------------------------------
// HTML 文字列をセットして、Shadow ツリーを構築する
// ------------------------------------------------------------
shadow_root.innerHTML = '<span style="color:#c00; background:#fee;" >あいうえお</span>';

// ------------------------------------------------------------
// BODY 要素のノードリストに登録する
// ------------------------------------------------------------
document.body.appendChild(element);
 
■ DOM API を使用する
 
ShadowRoot は、Node インターフェースを継承しています。
 
ShadowRoot のノードリストに、任意の DOM オブジェクトを登録します。
 
DOM API を使って、Shadow ツリーを構築する

// ------------------------------------------------------------
// DIV 要素を作成する
// ------------------------------------------------------------
var shadow_host = document.createElement("div");

// ------------------------------------------------------------
// ShadowRoot オブジェクトを作成する
// ------------------------------------------------------------
var shadow_root = shadow_host.attachShadow( { mode:"open" } );

// ------------------------------------------------------------
// SPAN 要素を作成する
// ------------------------------------------------------------
var element = document.createElement("SPAN");

// ------------------------------------------------------------
// テキストノードを作成する
// ------------------------------------------------------------
var text_node = document.createTextNode("あいうえお");

// ------------------------------------------------------------
// ShadowRoot のノードリストに登録する
// ------------------------------------------------------------
shadow_root.appendChild(element);
element.appendChild(text_node);

// ------------------------------------------------------------
// BODY 要素のノードリストに登録する
// ------------------------------------------------------------
document.body.appendChild(shadow_host);
 
■ <TEMPLATE> 要素を使用する
 
<TEMPLATE> タグ内に記述した子孫は、専用のサブツリーに登録されています。
 
これらの DOM オブジェクトは、不活性な状態となります。
 
例えば、<SCRIPT> 要素などは実行されません。
 
例えば、外部リソースへのアクセスは発生しません。
 
不活性な要素を動作させるには、通常のノードリストに再配置します。
 
<TEMPLATE> 要素内のコンテンツにアクセスするには、content プロパティを使用します。
 
DocumentFragment オブジェクトが得られます。
 
<TEMPLATE> 要素を使って、Shadow ツリーを構築する

<html>
  <body>

    <template id="my_template" >
      <span style="color:#c00; background:#fee;" >あいうえお</span> <br>
      <span style="color:#0c0; background:#efe;" >かきくけこ</span> <br>
      <span style="color:#c0c; background:#eef;" >さしすせそ</span> <br>
    </template>

    <div id="my_host" ></div>

    <script type="text/javascript">
    <!--

	// ------------------------------------------------------------
	// "my_template" という ID 属性のエレメントを取得する
	// ------------------------------------------------------------
	var template = document.getElementById("my_template");

	// ------------------------------------------------------------
	// "my_host" という ID 属性のエレメントを取得する
	// ------------------------------------------------------------
	var shadow_host = document.getElementById("my_host");

	// ------------------------------------------------------------
	// ShadowRoot オブジェクトを作成する
	// ------------------------------------------------------------
	var shadow_root = shadow_host.attachShadow( { mode:"open" } );

	// ------------------------------------------------------------
	// テンプレート内のコンテンツを複製する
	// ------------------------------------------------------------
	var content = template.content.cloneNode(true);

	// ------------------------------------------------------------
	// 複製したコンテンツを ShadowRoot 内に配置する
	// ------------------------------------------------------------
	shadow_root.appendChild(content);

    //-->
    </script>

  </body>
</html>
 

■ Shadow ツリーにアクセスする

 
■ DOM API を使用する
 
ShadowRoot は、Node インターフェースを継承しています。
 
ノードリストを巡って、Shadow ツリーにアクセスできます。
 
また、ShadowRoot は、以下のメソッドに対応しています。(一部抜粋)
 
メソッド名 説明
getElementById() ID 属性からエレメントを取得する
 

■ ShadowRoot 内のイベントについて

 
■ ShadowRoot 内の伝達について
 
ShadowRoot 内で発生したイベントの詳細な情報は、ShadowRoot まで伝達されます。
 
■ Shadow Host から祖先への伝達について
 
ShadowRoot の外側への伝達は、カプセル化されます。
 
Shadow Host 自体にイベントが発生したかのように通知されます。
 
■ ShadowRoot のイベントについて
 
ShadowRoot は、イベントリスナーのみ対応しています。
 
Element ではないので、イベントプロパティはありません。
 
■イベントの伝達順序を確認する
 
ShadowRoot 内のイベントの伝達順序を確認する

<html>
  <body>

    <template id="my_template" >
      <form id="my_form">
        <input type="button" value="a" >
        <input type="button" value="b" >
        <input type="button" value="c" >
      </form>
    </template>

    <div id="my_host" ></div>

    <script type="text/javascript">
    <!--

	// ------------------------------------------------------------
	// DOM オブジェクトのすべての子孫を検索する関数
	// ------------------------------------------------------------
	function DomNodeFindAllDescendants(node,func){
		function f(n){
			var nodes = n.childNodes;
			var i;
			var num = nodes.length;
			var a = new Array();
			for(i=0;i < num;i++){ a[i] = nodes[i]; }
			for(i=0;i < num;i++){
				node = func(a[i]);
				if(node){ return node; }
				node = f(a[i]);
				if(node){ return node; }
			}
			return null;
		}
		return f(node);
	}

	// ------------------------------------------------------------
	// "my_template" という ID 属性のエレメントを取得する
	// ------------------------------------------------------------
	var template = document.getElementById("my_template");

	// ------------------------------------------------------------
	// "my_host" という ID 属性のエレメントを取得する
	// ------------------------------------------------------------
	var shadow_host = document.getElementById("my_host");

	// ------------------------------------------------------------
	// ShadowRoot オブジェクトを作成する
	// ------------------------------------------------------------
	var shadow_root = shadow_host.attachShadow( { mode:"open" } );

	// ------------------------------------------------------------
	// テンプレート内のコンテンツを複製し、ShadowRoot 内に配置する
	// ------------------------------------------------------------
	var content = template.content.cloneNode(true);
	shadow_root.appendChild(content);

	// ------------------------------------------------------------
	// クリックされたときに実行される関数
	// ------------------------------------------------------------
	function MouseClickFunc (e){
		console.log(" ----- ");
		console.log("phase  :" , e.eventPhase);
		console.log("current:" , e.currentTarget);
		console.log("target :" , e.target);
	}

	// ------------------------------------------------------------
	// リッスンを開始する(Shadow Host から祖先)
	// ------------------------------------------------------------
	window.addEventListener("click" , MouseClickFunc , true);
	window.addEventListener("click" , MouseClickFunc , false);
	document.addEventListener("click" , MouseClickFunc , true);
	document.addEventListener("click" , MouseClickFunc , false);

	DomNodeFindAllDescendants(document,function(node){
		node.addEventListener("click" , MouseClickFunc , true);
		node.addEventListener("click" , MouseClickFunc , false);
		return null;
	});

	// ------------------------------------------------------------
	// リッスンを開始する(ShadowRoot 内)
	// ------------------------------------------------------------
	shadow_root.addEventListener("click" , MouseClickFunc , true);
	shadow_root.addEventListener("click" , MouseClickFunc , false);

	DomNodeFindAllDescendants(shadow_root,function(node){
		node.addEventListener("click" , MouseClickFunc , true);
		node.addEventListener("click" , MouseClickFunc , false);
		return null;
	});

    //-->
    </script>

  </body>
</html>
 

■ ShadowRoot 内のスタイルシートについて

 
■ ShadowRoot 内のスタイルシートについて
 
セレクタの検索範囲は、ShadowRoot 内に限定されます。
 
ShadowRoot 内のスタイルシートの適用範囲は、Shadow ツリーのみです。
 
■ ShadowRoot 内のスタイルシートをすべて取得する
 
styleSheets プロパティを使用します。
 
styleSheets プロパティから、CSSStyleSheetList オブジェクトが得られます。
 
スタイルシートについては、こちらで解説しています。
 
■ ShadowRoot 内のスタイルシートを確認する
 
ShadowRoot 内のスタイルシートを確認する

<html>
  <body>

    <template id="my_template" >

      <style type="text/css"> </style>
      <style type="text/css"> </style>
      <style type="text/css">
	#aaa{
		color:#c00; background:#fee;
	}
	#bbb{
		color:#0c0; background:#efe;
	}
	#ccc{
		color:#c0c; background:#eef;
	}
      </style>

      <div id="aaa" >あいうえお</div> <br>
      <div id="bbb" >かきくけこ</div> <br>
      <div id="ccc" >さしすせそ</div> <br>

    </template>

    <div id="my_host" ></div>

    <script type="text/javascript">
    <!--

	// ------------------------------------------------------------
	// "my_template" という ID 属性のエレメントを取得する
	// ------------------------------------------------------------
	var template = document.getElementById("my_template");

	// ------------------------------------------------------------
	// "my_host" という ID 属性のエレメントを取得する
	// ------------------------------------------------------------
	var shadow_host = document.getElementById("my_host");

	// ------------------------------------------------------------
	// ShadowRoot オブジェクトを作成する
	// ------------------------------------------------------------
	var shadow_root = shadow_host.attachShadow( { mode:"open" } );

	// ------------------------------------------------------------
	// テンプレート内のコンテンツを複製し、ShadowRoot 内に配置する
	// ------------------------------------------------------------
	var content = template.content.cloneNode(true);
	shadow_root.appendChild(content);

	// ------------------------------------------------------------
	// CSSStyleSheetList オブジェクトを取得する
	// ------------------------------------------------------------
	var sheet_list = shadow_root.styleSheets;

	// ------------------------------------------------------------
	// スタイルシートの総数を取得
	// ------------------------------------------------------------
	var num = sheet_list.length;

	// ------------------------------------------------------------
	// 出力テスト
	// ------------------------------------------------------------
	var i;
	for(i=0;i < num;i++){

		// CSSStyleSheet オブジェクトを取得する
		var style_sheet = sheet_list[i];

		// 出力テスト
		console.log(style_sheet);
	}

    //-->
    </script>

  </body>
</html>
 


 

ShadowRoot の機能について

 


■ ShadowRoot の機能について

 
■ ShadowRoot のプロパティ一覧
 
プロパティ名説明
hostElementShadow Host に該当する要素を取得する
innerHTMLStringShadowRoot 内のコンテンツを HTML 文字列で設定する
styleSheetsStyleSheetListCSSStyleSheet オブジェクトのリストを取得する
activeElementElementShadowRoot 内で、フォーカスを保持する要素を取得する
 
■ ShadowRoot のメソッド一覧(一部抜粋)
 
メソッド名 説明
getElementById() ID 属性からエレメントを取得する
elementFromPoint() クライアント座標を指定して、座標(点)と重なるエレメントを取得する
getSelection() Selection オブジェクトを取得する
 


 

Element の追加機能について

 


■ Element の追加機能について

 
Element インターフェースには、以下の機能が追加されます。
 
■ Element の追加プロパティ一覧
 
プロパティ名説明
shadowRootShadowRoot作成した ShadowRoot オブジェクトを取得する
 


 

<SLOT> 要素について

 
 


■ <SLOT> 要素について

 
<SLOT> 要素は、ShadowRoot 内で使用します。
 
<SLOT> 要素は、挿入ポイントの一種です。
 
■コンテント挿入ポイントについて
 
<SLOT> 要素には、Shadow Host 内に存在する要素を、代替表示できます。
 
ShadowRoot を作成すると、Shadow Host の子孫は非表示な状態となりますが、<SLOT> 要素を利用する事で、再び可視表示できるようになります。
 
■代替表示される要素について
 
代替表示される要素は、Shadow DOM としての恩恵はありません。
 
Shadow DOM ではなく、通常の DOM として動作します。
 

■ <SLOT> 要素の使用例

 
■ <SLOT> タグの記述例
 
<SLOT> 要素の記述例(ShadowRoot 内で使用する)

<slot name="aaa">
    名前と一致する要素が存在しませんでした
</slot>
 
■ name 属性値について(Shadow ツリー内)
 
<SLOT> 要素の name 属性に、任意の名前を指定します。
 
■ slot 属性値について(Shadow ホスト内)
 
表示したい要素の slot 属性に、同じ名前を指定します。
 
同じ名前が存在すれば、その要素が代替表示されます。
 
同じ名前が複数存在する場合、順番にすべて表示されます。
 
見つからなかった場合、<SLOT> 要素の子孫が、そのまま表示されます。
 
既に別の場所で代替表示されている場合、2回目以降はマッチしません。
 
■ <SLOT> 要素の使用例
 
<SLOT> 要素を使って、コンテンツを表示する

<html>
  <body>

    <div id="my_shadow_host" >

      <div slot="aaa" >私はホストAです</div>
      <div slot="bbb" >私はホストBです</div>

    </div>

    <script type="text/javascript">
    <!--

	// ------------------------------------------------------------
	// "my_shadow_host" という ID 属性のエレメントを取得する
	// ------------------------------------------------------------
	var shadow_host = document.getElementById("my_shadow_host");

	// ------------------------------------------------------------
	// ShadowRoot オブジェクトを作成する
	// ------------------------------------------------------------
	var shadow_root = shadow_host.attachShadow( { mode:"open" } );

	// ------------------------------------------------------------
	// HTML 文字列を指定して Shadow ツリーを構築する
	// ------------------------------------------------------------
	shadow_root.innerHTML =
		'<div id="container">' +
		'  <slot name="aaa">Aが見つからなかった</slot> <br>' +
		'  <slot name="bbb">Bが見つからなかった</slot> <br>' +
		'  <slot name="ccc">Cが見つからなかった</slot>' +
		'</div>';

	// ------------------------------------------------------------
	// ShadowRoot 内の content 要素を取得する
	// ------------------------------------------------------------
	var container = shadow_root.getElementById("container");
	var slot_list = container.getElementsByTagName("slot");
	var slot0 = slot_list[0];
	var slot1 = slot_list[1];
	var slot2 = slot_list[2];

	// ------------------------------------------------------------
	// slot 要素に現在表示されている DOM オブジェクトを取得する
	// ------------------------------------------------------------
	console.log( slot0.assignedNodes() );
	console.log( slot1.assignedNodes() );
	console.log( slot2.assignedNodes() );

    //-->
    </script>

  </body>
</html>
 

■ <SLOT> 要素の機能について

 
■ <SLOT> 要素のプロパティ一覧
 
プロパティ名説明
nameString代替表示したい Shadow Host 内の要素を、名前で指定する。
 
■ <SLOT> 要素のメソッド一覧
 
メソッド名 説明
assignedNodes() 自身に現在表示されている DOM オブジェクトを、NodeList 形式で取得する。
代替表示に成功している場合、その要素が得られる。