Tender Surrender

Shadow DOM - Web Components を構成する技術

この記事は webcomponents.org の記事とのクロスポストです。

Shadow DOM を利用すると、DOM 要素に、ウェブページの他の部分とは切り離された、ノード内だけで有効なスタイルやマークアップを含んだ DOM ツリーを追加することができます。この記事と動画では、この Shadow DOM について解説します。

Shadow DOM とはなにか?

こちらは HTML5 の video タグで表示された動画です。ご覧頂けるとお分かりのように、コードは video タグのみという単純さでありながら、動画そのものだけでなく、制御用の UI も表示することができています。

1
<video src="http://craftymind.com/factory/html5video/BigBuckBunny_640x360.mp4" controls></video>

実は Chrome で DevTools を開いて、'Show user agent shadow DOM' オプションを on にすると、この制御用 UI がどのようにできているか確認することができます。

この制御用 UI が、実際はHTML でできていることがお分かりでしょうか?これが Shadow DOM の一例です。

Shadow DOM が素晴らしいのは、実はこの機能がウェブ開発者にも使える、ということです。

Shadow DOM の構造

Shadow Root を持った要素は Shadow Host と呼ばれます。Shadow Root は通常の DOM 要素と同様に扱えるため、任意のノードを追加することもできます。

Shadow DOM では、すべてのマークアップと CSS が要素内にスコープされます。 言い換えると、Shadow Root 内で定義された CSS は親ドキュメントに影響を与えず、親ドキュメントの CSS が誤って Shadow Root 内に影響を与えることもありません。

Shadow DOM の作り方

Shadow DOM を作るには、任意の DOM 要素に対して .createShadowRoot() を呼び出し、Shadow Root を作ります。この Shadow Root オブジェクトに要素を足していくことで、Shadow DOM を構築していくことができます。

1
<div id="host"></div>
1
2
3
4
5
var host = document.querySelector('#host');
var root = host.createShadowRoot(); // Shadow Root を作る
var div = document.createElement('div');
div.textContent = 'This is Shadow DOM';
root.appendChild(div); // Shadow Root に要素を追加

Shadow Root に追加された要素はクエリすることもできません。この場合、document.querySelector('#host div') は null になります。

Shadow DOM 内に Shadow Host のコンテンツを表示する

Shadow DOM 内に Shadow Host の子要素を表示したい場合があると思います。  例えば Shadow DOM によってスタイルを与えられているネームタグのような要素を考えてみましょう。外部からの入力で文字だけ変更できると便利ですよね。

1
<div id="nameTag">Bob</div>

これを実現するには、<content> 要素を用います。

1
2
3
4
5
var host = document.querySelector('#host');
var root = host.createShadowRoot();
var content = document.createElement('content');
content.setAttribute('select', 'h1'); //<content select="h1"></content>
root.appendChild(content);
1
2
3
<div id="host">
  <h1>This is Shadow DOM</h1>
<div>

<content> 要素に select 属性として Shadow Host から取り上げたいノードを指し示す CSS セレクタを与えることで、その要素が <content> の位置に挿入されます。

なお、<content> 要素で指定できるのは、Shadow Host の直接の子孫となる要素を示す CSS セレクタのみです。つまり以下のように、子孫の子孫を示すようなことはできません。

1
2
3
4
5
6
7
<div id="host">
  <div class="child">
    <h1>This is Shadow DOM</h1>
  </div>
</div>

<content select=".child h1"></content> // これはダメ

Template と組み合わせる

Shadow DOM は素晴らしいですが、構築のためにいちいち命令的に DOM ツリーを構築する JavaScript を書くのは楽ではありませんし、デザイナーが入ってくる余地もありません。

そこで登場するのが Template 要素です。Template 要素を活用することで、Shadow DOM の構築が宣言的に行いましょう。Template 要素については、前回のポストを参考にして下さい。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template id="template"> // <template> の中身が Shadow DOM になる
  <style>
    ...
  </style>
  <div id="container">
    <img src="http://webcomponents.org/img/logo.svg">
    <content select="h1"></content> // h1 をここに挿入
  </div>
</template>

<div id="host">
  <h1>This is Shadow DOM</h1>
</div>
1
2
3
4
5
6
7
8
var host = document.querySelector('#host');
// Shadow Root を作る
var root = host.createShadowRoot();
var template = document.querySelector('#template');
// <template> をコピー
var clone = document.importNode(template.content, true);
// Shadow Root に追加
root.appendChild(clone);

実際のコードはこちらでご覧頂けます。

ブラウザサポート状況

Shadow DOM は 2014 年 10 月現在 Chrome, Opera, フラグ付きなら Firefox でもサポートされています。最新のサポート状況は chromestatus.com または caniuse.com でチェックしてみて下さい。ポリフィルとして platform.js (2014 年 11 月から webcomponents.js に名称変更予定) も利用できます。

まとめ

いかがでしたでしょうか?Shadow DOM は今回記事にした内容の他にも、外部からのスタイリングやイベントの扱い方、複数の Shadow Root の扱い方など、非常に複雑な仕様が盛りだくさんです。

Shadow DOM についてより詳しく知りたいという方は、下記のドキュメントを参考にしてください。

comments powered by Disqus