Web Componentsの構成要素まとめ（応用編）

[前回記事](http://qiita.com/masashi0127/items/9d81b0396f4834062ead)の応用で、Shadowの特徴のまとめ記事

# Shadow の特徴
- 複数のShadow Root
- 挿入ポイント（Insert Points）と分散ノード（Distributed Nodes）
- イベントの設定
- 参考

## 複数のShadow Root

- 同じホストからの生成された複数のShadow Rootに、別々に要素を追加すると、最後に追加した要素のみが出力される。

```html
<script>
window.onload = function() {
  var container = document.getElementById('container');
  var rootFirst = container.createShadowRoot();
  var rootSecond = container.createShadowRoot();
  rootFirst.innerHTML = '<div>This is first root</div>'; //表示されない
  rootSecond.innerHTML = '<div>This is second root</div>'; //表示される
};
</script>
<div id="container"></div>
```

- 同じホストから生成された複数のShadow Rootの要素を出力するためには`<shadow>`要素を使用する。
- `<shadow>`要素内には直前に追加された要素が出力される。

```html
<script>
window.onload = function() {
  var container = document.getElementById('container');
  var rootFirst = container.createShadowRoot();
  var rootSecond = container.createShadowRoot();
  rootFirst.innerHTML = '<div>This is first root</div>'; //secondRootの<shadow>タグの中に表示される。
  rootSecond.innerHTML = '<shadow></shadow><div>This is second root</div>'; //表示される。
};
</script>
<div id="container"></div>
```

- Shadow Rootのホスト要素（`<div id="container">`）の内容を出力するためには`<content>`タグを使用する。

```html
<script>
window.onload = function() {
  var container = document.getElementById('container');
  var rootFirst = container.createShadowRoot();
  var rootSecond = container.createShadowRoot();
  rootFirst.innerHTML = '<div>This is first root</div>'; //secondRootの<shadow>タグの中に表示される。
  rootSecond.innerHTML = '<shadow></shadow><div>This is second root</div><content></content>'; //表示される。
};
</script>
<div id="container">This is container</div> <!-- "This is container"はrootSecondの<content>タグの中に表示される。 -->
```

## 挿入ポイント（Insert Points）と分散ノード（Distributed Nodes）

- `<shadow>`や`<content>`要素を**挿入ポイント（Insert Points）**という。
- `<shadow>`や`<content>`要素に表示される要素を**分散ノード（Distributed Nodes）**という。
- `<shadow>`や`<content>`要素の子要素として分散ノードにアクセスすることはできない。

```html
<script>
window.onload = function() {
  var container = document.getElementById('container');
  var rootFirst = container.createShadowRoot();
  var rootSecond = container.createShadowRoot();
  rootFirst.innerHTML = '<div>This is first root</div>';
  rootSecond.innerHTML = '<shadow></shadow><div>This is second root</div><content></content>';
  
  console.log(rootSecond.querySelector('content p')); // null
};
</script>
<div id="container"><p>This is container</p></div>
```

- **getDistributedNodes()** を使用して、挿入ポイントに表示される分散ノードにアクセスできる。
- **getDestinationInsertionPoints()** を使用して、分散ノードが表示される挿入ポイントにアクセスできる。

```html
<script>
window.onload = function() {
  var container = document.getElementById('container');
  var rootFirst = container.createShadowRoot();
  var rootSecond = container.createShadowRoot();
  rootFirst.innerHTML = '<div>This is first root</div>';
  rootSecond.innerHTML = '<shadow></shadow><div>This is second root</div><content></content>';

  console.log(rootSecond.querySelector('content').getDistributedNodes()); //<p>This is container</p>
  console.log(container.querySelector('p').getDestinationInsertionPoints()); //<content></content>
};
</script>
<div id="container"><p>This is container</p></div>
```

## イベントの設定

- document要素からShadow DOMにイベントを設定することはできない。
- Shadow DOMに対するイベントは、全てホスト要素から発生するように再調整される。

```html
<input type="text" placeholder="ホスト">
<div id="container"><p>content</p></div>
<script>
  window.onload = function() {
    var host = document.getElementById('container');
    var root = host.createShadowRoot();
    root.innerHTML = '<input type="text" placeholder="Shadow DOM">' +
      '<p id="one">One</p>' +
      '<p id="two">Two</p>' +
      '<content></content>' +
      '<p id="three">Three</p>' +
      '<p id="four">Four</p>';

    //Shadow DOM内の要素をクリックすると<div id="container">要素がターゲットになっている。
    document.addEventListener('click', function(e) { 
      console.log(e.target);
    });

    //Shadow DOM内のinput要素をクリックしても発生しない。
    //ホストのinput要素をクリックすると発生する。
    document.querySelector('input').addEventListener('click', function(e) {
      console.log(e.target);
    });
  }
</script>
```

- Shadow DOMにイベントを設定する場合は、Shadow Rootから対象要素を取得して設定する。

```html
<input type="text" placeholder="ホスト">
<div id="container"><p>content</p></div>
<script>
  window.onload = function() {
    var host = document.getElementById('container');
    var root = host.createShadowRoot();
    root.innerHTML = '<input type="text" placeholder="Shadow DOM">' +
      '<p id="one">One</p>' +
      '<p id="two">Two</p>' +
      '<content></content>' +
      '<p id="three">Three</p>' +
      '<p id="four">Four</p>';

    //Shadow DOM内のinput要素をクリックすると発生する。
    //ホストのinput要素をクリックしても発生しない。
    root.querySelector('input').addEventListener('click', function(e) {
      console.log(e.target);
    });
  }
</script>
```

# 参考
[Shadow DOM 201](http://www.html5rocks.com/ja/tutorials/webcomponents/shadowdom-201/)
[Shadow DOM 301](http://www.html5rocks.com/ja/tutorials/webcomponents/shadowdom-301/)
[W3C Shadow DOM](http://w3c.github.io/webcomponents/spec/shadow/#shadow-trees)