42
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Web Componentsの構成要素まとめ(基本編)

Last updated at Posted at 2014-08-29

Web Components の構成要素の特徴についてのまとめ

構成要素

  1. Custom Elements
  2. Template
  3. Shadow DOM
  4. HTMLImports

Custom Elements

  • 任意の名前の要素を作成できる。
  • 要素名には-が必須
<script>
var proto = Object.create(HTMLElement.prototype);
document.registerElement('my-element', {prototype: proto});
</script>

<my-element></my-element>

Template

  • <template>を使ってテンプレートを作成できる。
  • <template>内に記述した内容はDOMとして認識されないため、JSの実行やCSSの読み込みは起こらない。
  • 別のDOM要素内に取り込まれて初めてレンダリングされる。
<script>
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
  var template = document.getElementById('thisIsTemplate');
  var templateClone = document.importNode(template.content, true);
  this.appendChild(templateClone);
}
document.registerElement('this-is-template', {prototype: proto});
</script>
<template id="thisIsTemplate">
<div>This is Template</div>
</template>

<this-is-template></this-is-template>

Shadow DOM

  • カプセル化した要素を作成できる。
  • JSやCSSの影響を限定できる。
<script>
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
  var root = this.createShadowRoot();
  root.innerHTML = '<div class="shadow">This is shadow</div>';
}
document.registerElement('this-is-shadow', {prototype: proto});
</script>

<this-is-shadow></this-is-shadow>

HTML Import

  • 外部HTMLファイルの要素を取り込むことができる。
index.html
<script>
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
  var link = document.querySelector('link[href="import.html"]');
  var template = link.import.getElementById('thisIsTemplate');
  var templateClone = document.importNode(template.content, true);
  var root = this.createShadowRoot();
  root.appendChild(templateClone);
}
document.registerElement('this-is-import', {prototype: proto});
</script>

<this-is-import></this-is-import>
import.html
<template id="thisIsTemplate">
  <div>This is Import</div>
</template>

基本的な使い方

プロトタイプの生成

var poroto = Object.create(HTMLElement.prototype);
  • 要素のプロトタイプをインスタンス化
  • 新しいを要素を作成する場合、HTMLElement.prototypeを渡す。
  • 既存の要素を拡張する場合、HTML____Element.prototype(目的の要素インターフェースのプロトタイプ)を渡す。

コールバック

var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {...}

コールバックは下記の4つがある。

コールバック名 タイミング
createdCallback 要素のインスタンスが作られた時
attachedCallback インスタンスがドキュメントに追加された時
detachedCallback インスタンスがドキュメントから取り除かれた時
attributeChangedCallback(attrName, oldVal, newVal) 属性が追加、削除、更新された時

テンプレートのインポート

var template = document.getElementById('template');
var templateClone = document.importNode(template.content. true);

Shadow DOM の使用

proto.createdCallback = function() {
  ...
  var root = this.createShadowRoot();
}
  • thisprotoを取り込む要素
  • つまりdocument.registerElement()の第一引数にセットする要素

新規要素の生成

document.registerElement('my-element', {prototype: proto});

既存要素の拡張

<script>
var proto = Object.create(HTMLButtonElement.prototype);
proto.createdCallback = function() {
  this.addEventListener('click', function() {
    alert('alert-button is clicked!');
  });
}
document.registerElement('alert-button', {
  prototype: proto,
  extends: 'button'
});
</script>
<button is="alert-button"></button>

まとめ

  • 要素の元(Object.create())に<template>の内容を取り込んで、その元を自分の好きな要素名にして(document.registerElement())表示する。
  • 「コンポーネントのカプセル化」を実現するためには、ShadowRootを生成してそこに要素を取り込む。
<html>
  <head>
    <style>
      div {
        color: red;
      }
    </style>
    <link rel="import" href="import.html">
    <link rel="import" href="library.html">
    <script src="platform.js"></script>
    <script>
      // Create Elementsの機能で新しい要素を生成
      var proto = Object.create(HTMLElement.prototype);
      proto.createdCallback = function() {
        this.innerHTML = '<div>This is normal</div>';
      }
      document.registerElement('this-is-normal', {prototype: proto});
      
      // Shadow DOMの機能を使って要素をカプセル化
      // CSSがShadow DOM要素内のみに適用される。
      var protoShadow = Object.create(HTMLElement.prototype);
      protoShadow.createdCallback = function() {
        var root = this.createShadowRoot();
        root.innerHTML = '<div class="shadow"><style>div {color: red;}</style>This is shadow</div>';
      }
      document.registerElement('this-is-shadow', {prototype: protoShadow});
      

      // Template機能を使ってテンプレート要素を読み込む。
      // カプセル化されていないなので、appendChildで取り込まれるとCSSが全体に影響する。
      var protoTemplate = Object.create(HTMLElement.prototype);
      protoTemplate.createdCallback = function() {
        var template = document.getElementById('thisIsTemplate');
        var templateClone = document.importNode(template.content, true);
        this.appendChild(templateClone);
      }
      document.registerElement('this-is-template', {prototype: protoTemplate});
      
      // TemplateとShadow DOM機能を使ってテンプレート内容をカプセル化
      // テンプレート内の要素にのみCSSが適用される。
      var protoShadowTemplate = Object.create(HTMLElement.prototype);
      protoShadowTemplate.createdCallback = function() {
        var template = document.getElementById('thisIsShadowTemplate');
        var templateClone = document.importNode(template.content, true);
        var root = this.createShadowRoot();
        root.appendChild(templateClone);
      }
      document.registerElement('this-is-shadow-template', {prototype: protoShadowTemplate});
      
      // HTML Imports機能を使って外部HTMLファイルのテンプレート内容を取り込む。
      var protoImportShadowTemplate = Object.create(HTMLElement.prototype);
      protoImportShadowTemplate.createdCallback = function() {
        var link = document.querySelector('link[href="import.html"]');
        var template = link.import.getElementById('thisIsImportShadowTemplate');
        var templateClone = document.importNode(template.content, true);
        var root = this.createShadowRoot();
        root.appendChild(templateClone);
      }
      document.registerElement('this-is-import-shadow-template', {prototype: protoImportShadowTemplate});
      
      // 外部HTMLファイルのテンプレート内にライブラリのリンクをまとめて記述しておけば一括で取り込める。
      var protoImportLibrary = Object.create(HTMLElement.prototype);
      protoImportLibrary.createdCallback = function() {
        var link = document.querySelector('link[href="library.html"]');
        var template = link.import.getElementById('thisIsImportLibrary');
        var templateClone = document.importNode(template.content, true);
        this.appendChild(templateClone);
      }
      document.registerElement('this-is-import-library', {prototype: protoImportLibrary});
    </script>
    
    <template id="thisIsTemplate">
      <style>div {color: blue;}</style>
      <div>This is Template</div>
    </template>
    
    <template id="thisIsShadowTemplate">
      <style>div {color: green;}</style>
      <div class="shadow">This is TemplateShadow</div>
    </template>
  
  </head>
  <body>
    <this-is-normal></this-is-normal>
    <this-is-shadow></this-is-shadow>
    <this-is-template></this-is-template>
    <this-is-shadow-template></this-is-shadow-template>
    <this-is-import-shadow-template></this-is-import-shadow-template>
    <this-is-import-library></this-is-import-library>
  </body>
</html>
import.html
<template id="thisIsImportShadowTemplate">
  <style>div {color: gray;}</style>
  <div>This is Import</div>
</template>
library.html
<template id="thisIsImportLibrary">
  <div>This is Import Library</div>
  <link rel="stylesheet" href="font-awesome-4.1.0/css/font-awesome.min.css">
</template>

参考

Web開発者に革命をもたらす!「Web Components」超入門
なぜ Web Components はウェブ開発に革命を起こすのか
Shadow DOM 101
Shadow DOM 201

42
40
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?