Web Components最新事情 2014 第1回 Web Componentsの仕様

より複雑化するフロントエンド実装の設計に進歩をもたらすべく策定中のWeb Componentsの最新情報を紹介。第1回目はWeb Componentsを構成する仕様群とその概要を解説します。

発行

著者 山田 順久 フロントエンド・エンジニア
Web Components最新事情 2014 シリーズの記事一覧

はじめに

CodeGridでは以前に「先取り、Shadow DOM - Shadow DOMが生まれた理由」という記事に始まる、現在でも仕様策定中であるShadow DOMについて取り上げたことがありました。

そして、これと同じシリーズの連載記事ではWeb Componentsという、より大きな技術的な枠組みについても「Web Components その1」、「Web Components その2」として取り上げました。

しかし、2年近く前に書かれたこれらの記事には陳腐化してしまった記述もあります。Shadow DOMの有効性は今も変わりませんが、現在ではDecorator仕様が策定作業からはずれましたし、すでに現在のブラウザでは動かなくなってしまった動作デモも多くあります。

これらの記事が書かれていた頃から現在に至るまで、Web Components関連仕様の検討は続けられており、ブラウザ側での実装も形を変えながら、以前と比べるとより多くの機能が実際に動くようになっています。さらに今年(2014年)の6月に開催されたGoogle I/O 2014でWeb Componentsは大きく取り上げられ、その認知度も大きく向上したように思います。

そうした背景を踏まえて、この記事ではWeb Components最新事情と題して、2014年現在の仕様に沿った内容で、Web Componentsの利点やサンプルコードなどの紹介をしていきたいと思います。

反対に、この記事で深く取り上げない内容としては、Googleが開発しているWeb ComponentsのPolyfillおよびUIライブラリであるPolymerがあります。PolymerはWeb Componentsをサポートしていないブラウザに対してもWeb Components同等の挙動を実現するためのPolyfillとして機能しますが、Googleが提供するUIライブラリであるという側面も持っています。

また、Web Componentsの機能をさりげなく拡張している部分もあり、「標準仕様」としてのWeb Componentsの現状を取り上げていくにあたって、その輪郭がぼやけてしまうおそれもあります。そのような理由から、この記事ではPolymerについては深く掘り下げず、純粋にWeb Componentsの仕様について追っていきたいと思います。

Web Componentsとは

まずは、Web Componentsとは何であるかについて改めて触れていきたいと思います。Web Componentsはその名前の通り、Webページを構成するUIなどのパーツをコンポーネント化するための仕様の総称です。

例えばタブでコンテンツを切り替えるUIを実装する場合、だいたい次のようなHTMLを書くことになると思います。これはjQuery UIのタブUIを使用する際に必要となるコード例の一部分です。

<div id="tabs">
  <ul>
    <li><a href="#tabs-1">タブ1</a></li>
    <li><a href="#tabs-2">タブ2</a></li>
    <li><a href="#tabs-3">タブ3</a></li>
  </ul>
</div>

<script>
  $(function() {
    $('#tabs').tabs();
  });
</script>

このほかにもCSSファイルやJSファイルなども、次のようにHTMLから読み込む必要があるでしょう。

<link rel="stylesheet" href="//code.jquery.com/ui/1.11.1/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.1/jquery-ui.js"></script>

このタブUIをWeb Componentsで表現した場合には、次のような形が考えられます。仕様の詳細は後述しますので、ここでは概要を掴んでください。

<x-tabs>というタグが登場していますが、これはWeb Componentsを支える仕様の1つであるCustom Elementsの機能によって、新たなHTML要素を定義することで実現できます。

<x-tabs selected="1">
  <x-tab>タブ1</x-tab>
  <x-tab>タブ2</x-tab>
  <x-tab>タブ3</x-tab>
</x-tabs>

そして、この<x-tabs>コンポーネントを利用するためのリソース読み込みは、この一行だけで十分です。CSSやJSそれぞれを別個に読み込む記述を加える必要はありません。こちらもWeb Componentsを構成する仕様のひとつであるHTML Imports仕様の実装によって実現します。

<link rel="import" href="path/to/tabs.html">

このHTMLコードを見てみると、<div>を使って表現するのではなく<x-tabs>という名前から、このHTMLコードはタブを表しているのだという役割がはっきりしています。

HTML構造もそれぞれのタブ項目を表す<x-tab>と、それらをまとめる<x-tabs>しかありません。この<x-tabs>コンポーネントを利用した場合には、<div>がこういう入れ子になっていて……といったような構造を知る必要はなく、<x-tabs><x-tab>という2つのタグを使ってタブUIが作れるということだけを知っていれば十分なのです。

しかし、この構造では<ul><li>しか使わないのと同じようなものにも見えるかもしれません、これについては後述しますが、実際にはこの<x-tabs><x-tab>の中には、前者のコード例と同等、あるいはもっと複雑なHTML構造が隠れているのです。

それから、前者のコード例ではタブのイベントリスナなどを初期化するために$('#tabs').tabs()といったメソッドを呼び出す必要があるわけですが、Web Componentsを利用した場合、そういった初期化処理やそのタイミングも<x-tabs>コンポーネント側の責任として明確に切り分けることができます。

<x-tabs>コンポーネントの初期化については、それを配置するHTML側からは何も関心を抱く必要はありません。現在でも<select>などはHTMLファイルに書くだけでプルダウン形式の選択肢を選ぶUIを提供してくれますが、それと同じことです(もちろん、<x-tags>コンポーネントの作成者は求められる挙動に対して責任を果たす必要があり、その方法もWeb Componentsには用意されています)。

Web Componentsを支える仕様群

Web Componentsを構成するそれぞれの仕様についても簡単に紹介していきます。

Custom Elements

先ほどの例でも述べましたが、Custom Elements*は新たなHTML要素の定義を可能にします。要素はdocument.registerElement()を呼び出して、任意の名前で登録することができます。名前には-(ハイフン)を間に挟む必要があります。

*注:Custom Elements

詳しい仕様は「Custom Elements」を参照してください。2014年8月現在の最新版は、2013年10月24日付け最終草案です。

document.registerElement('x-hoge');

こうして登録した要素はHTMLやJavaScriptから、ほかのHTML要素と同じように使用することができます。

HTMLから使用する例:

<x-hoge></x-hoge>

JavaScriptから利用する例:

var hogeElement = document.createElement('x-hoge');

HTML Templates

HTML Templates*はブラウザにレンダリングされないHTMLテンプレートをHTMLファイル内に記述することができます。

*注:HTML Templates

詳しい仕様はHTML 5の「4.11.3 The template element」を参照してください。

<template id="hogeTemplate">
  <div class="hoge">
    hoge
  </div>
</template>

<template>内に記述されたHTMLは、普通にレンダリングされているDOMツリーへ追加されるまで、スクリプトの動作や画像のロードといった副作用を起こしません。また、document.querySelector()などのDOM APIの影響も<template>内の子要素に対して働くことはありません。

現在でも次のように<script>タグの中にHTMLを書いておく手法が見られますが、それを標準仕様として整備したものになります。

<script type="text/template" id="hogeTemplate">
  <div class="hoge">
    hoge
  </div>
</script>

Shadow DOM

Shadow DOM*は対象のDOMツリーの実態を、通常のDOMツリーから隔離された領域に隠蔽します。

*注:Shadow DOM

詳しい仕様は「Shadow DOM」を参照してください。2014年8月現在の最新版は、2014年6月17日付け草案です。

冒頭の説明で<x-tabs><x-tab>には複雑なHTML構造が隠れていると述べたのは、このShadow DOMのことを指しています。

例えば、次のようなCustom Elementsを作成してHTML上に記述したとします。

<x-hode></x-hoge>

HTML上には<x-hoge>という記述しか行いませんが、それがShadow DOMを利用している場合には<x-hoge>要素の見た目上のHTML構造を別途用意することができます。Shadow DOM内に隠蔽された要素はChromeのデベロッパー ツールなどから確認することができ、#shadow-root以下の要素がShadow DOM内に隠蔽されている要素を表しています。

<x-hoge>
  #shadow-root
    <div id="hoge">
      <div id="hogeContent">
        <p>Hoge</p>
      </div>
    </div>
</x-hoge>

コンポーネントを利用する側の視点から複雑なHTMLを隠すことができる上に、CSSセレクタやdocument.querySelector()などのDOM APIの影響範囲の面においても通常のDOMから隔離された領域にあるため、お互いに影響を与えることがありません。例えばコンポーネント利用側で定義されたCSSルールは、Shadow DOM内の要素に適用されません。そしてその逆も同様です。

そのおかげでコンポーネントの利用側はコンポーネント内部の実装を気にすることなく、その反対にコンポーネント作成側は利用側の実装を気にすることなく独立性を持ったモジュールの開発に専念できるようになるのです。

HTML Imports

HTML Imports*はHTMLから外部のHTML読み込んで利用することができる仕様です。

*注:HTML Imports

詳しい仕様は「HTML Imports」を参照してください。2014年8月現在の最新版は、2014年3月11日付け草案です。

これも冒頭の説明で登場していたものです。次のように<link>タグにrel="import"属性を宣言することでHTMLの読み込みができます。

<link rel="import" href="path/to/hoge.html">

これを使うことでインポート元のHTMLにWeb Componentsを定義しておき、インポート先では上記の<link>タグを記述するだけで、新たに定義されたHTML要素を使うことができます。

Web Componentsのメリット

Web Componentsのメリットとして挙げられるものとしては、Shadow DOMによる実装の隠蔽によってコンポーネントの利用側と実装側で関心事の分離が、これまでよりもしっかりと行えるという点が大きいのではないかと考えています。

現在のHTMLではDOM APIを通じた要素へのアクセスや、CSSのルールが影響を及ぼす範囲をコントロールすることができません。ちょうどすべてがグローバル変数で定義されているかのように、ある場所の変更が別の思わぬ場所に副作用をもたらしてしまうかもしれないのです。

Shadow DOMを使って、Web ComponentsをインポートしているHTMLドキュメントから隔離されたDOMツリーを生成しておけば、その中でどのようなid属性、class属性やCSSルールを記述しても、その影響が及ばないインポート先のことを考慮する必要はありません。

Shadow DOMが使われたWeb Componentsの振る舞いや見た目の実装は、インポート先HTMLの影響を受けることがなくなり、一切はWeb Components側の責任として実装されることになります。これはインポート先の責任負担が減ることも意味します。なぜなら、自分の書いたコードが今使っているタブUIに悪影響を与えるのではないか? といったようなことをいちいち悩まなくて済むようになるからです。

加えて、Web Componentsは標準仕様として策定が進められているので、Web Componentsを利用する際には、その仕様に基づいた方法を覚えれば、ほかの多くのコンポーネントも同じように利用できるという利点も考えられます。

jQuery UIのTabsコンポーネントを初期化するには$('#tabs').tabs()というメソッドの呼び出しが必要ですが、別のライブラリが提供するタブUIを使用する際にはnew Tabs(document.querySelector('#tabs')).start();のような呼び出し方になるかもしれません。標準仕様として策定されれば、そういったばらつきがなくなることも見込めます。

Web Componentsの現状

そんなWeb Componentsの現状ですが、Chrome 36でHTML Importsの実装がなされたことでWeb Componentsを構成する仕様のすべてがChromeのStable Channel(安定版)でも使うことができるようになりました。

そのほかのブラウザについては、caniuse.comによる各ブラウザのWeb Components仕様の実装状況に基づくと、ChromeやFirefox、またChromeと同じくBlinkエンジンを採用するOperaの実装が追いついてきています。

まだ実装のないInternet Explorerについてもstatus.modern.ieのWebサイトから検討中項目として挙げられていることが確認できます。

次回からはWeb Components仕様が一番多く実装されているChromeを対象にしたWeb Componentsの作成を紹介していきたいと思います。