ざっくりWeb Components
今年の2月、Fukuoka Engineers Day 2018というイベントで「Web Componentsの現在地」というタイトルで発表しました。(まとめはこちら)
そこから半年、Firefoxでもバージョン63でついにすべての仕様がサポートされるらしいので、あらためてWeb Componentsの仕様のおさらいメモ。
Web Componentsとは
Web ComponentsはWeb標準の技術のみで、再利用可能な部品を作るためのWeb APIです。JSフレームワークを導入して実現していた独自のコンポーネントを標準APIのみで実現できるようになります。
(とはいえ、Web ComponentsとJSフレームワークは競合する考え方ではなく、組み合わせて利用可能なものです。Web Componentsが使えるようになるからJSフレームワークは不要になる!という類のものではないです。念のため。)
Web Componentsそのものが仕様を指すわけではなく、
- HTML templates
- Custom Elements
- Shadow DOM
の3つの仕様をまとめてそう呼んでいます。
HTML templates
HTMLのtemplateタグのことです。ページの初期表示時にはレンダリングされず、必要に応じてJavaScriptから操作してDOMに追加します。
<template> <h1>HTML templates Demo </template> <div id="container"></div>
const template = document.getElementById('demo').content; const container = document.getElementById('container'); const node = template.cloneNode(true); container.appendChild(node);
Custom Elements
HTMLに独自のタグを追加するAPIです。customElements.define
を使って定義します。
Shadow DOM
グローバルなDOMツリーから分離された、カスタム要素に閉じたDOMツリーを持つことができます。CSSなど、グローバルに影響を与えずに、カスタム要素内で独自にスタイリングしたり、処理を行ったりできるようになります。
対応ブラウザ
今のところ、上記の仕様をすべて実装している(=既にWeb Componentsが使える)ブラウザはGoogle Chrome、Safari(デスクトップ、モバイル)です。Firefoxはバージョン63で対応予定、EdgeはHTML templatesのみ対応、IEは全ての仕様に未対応です。
課題はEdgeとIEですが、webcomponents.js
というPolyfillを利用することでShadow DOM以外の仕様をエミュレートすることができます。(Shadow DOMはパフォーマンスの問題でoffになっています。)
とはいえShadow DOMはすごく魅力的な仕様なので、これが使えないのは惜しいと言わざるを得ないです。(実際の動作はこの後のサンプルを確認して下さい。)
書いてみる
最小のサンプル
とりあえず最小のサンプルです。
class MyElement extends HTMLElement { connectedCallback() { const shadowRoot = this.attachShadow({ mode: 'open' }); const h1 = document.createElement('h1'); h1.textContent = 'Web Components Test'; shadowRoot.appendChild(h1); } } customElements.define('my-element', MyElement);
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="index.js"></script> </head> <body> <my-element></my-element> </body> </html>
上記をそれぞれindex.html, index.jsと言う名前で保存して開いてみてください。(当然ですがWeb Componentsの仕様に対応したブラウザで確認して下さい)
手順としてはHTMLElement
を継承したクラスを定義して、connectedCallback
イベントでShadow DOMを有効化(attachShadow({ mode: 'open' })
)し、通常のDOM操作同様にappendChild
して要素を追加する。定義したクラスをcustomElements.define
に渡してCustom Elementsを定義する、という流れです。
Shadow DOMによるScoped CSS
Shadow DOMによってCSSの影響が分離されていることを確認してみます。JavaScriptはそのままで、index.htmlを以下のように変更します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="index.js"></script> <style> h1 { color: red; } </style> </head> <body> <my-element></my-element> <h1>DOM Tree</h1> </body> </html>
このHTMLを表示すると、以下のように表示されます。
通常のh1要素は文字色が変更されていますが、カスタム要素のh1要素には効いていないことが分かります。
これは逆も同様です。
class MyElement extends HTMLElement { connectedCallback() { const shadowRoot = this.attachShadow({ mode: 'open' }); const styles = document.createElement('style'); styles.textContent = ` h1 { color: red; } `; shadowRoot.appendChild(styles); const h1 = document.createElement('h1'); h1.textContent = 'Web Components'; shadowRoot.appendChild(h1); } } customElements.define('my-element', MyElement);
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="index.js"></script> </head> <body> <h1>DOM Tree</h1> <my-element></my-element> </body> </html>
このように変更して結果を確認すると以下のようになります。
見ての通り、h1タグを指定してスタイルを適用しても、カスタム要素の外には影響がありません。
ChromeのDeveloper Toolで確認すると以下のようにレンダリングされています。
このように、styleタグがshadow-rootの中にレンダリングされており、影響範囲がカプセル化できていることが分かります。
まとめ
- Web ComponentsはWeb標準のAPIを利用して再利用可能な部品を作るための仕組み
- Web Components自体がAPIを指すのではなく、いくつかの仕様の総称
- そろそろWeb ComponentsがFirefoxでも使えるように!
- Shadow DOMによってWeb標準の要素によってScoped CSSが使える未来も近い
- Edgeは実装はよして
- IEはサポート打ち切りはよして
Web ComponentsとJSフレームワークについては別の記事で書ければ。では。