背景
マークアップを行う際に、決まった要素のまとまりを使い回したいと思った時に、JSのフレームワークのReactやVue.jsを利用するとコンポーネントとして再利用可能な部品を作る事ができるのですが、学習コストがかかってしまったり、案件のサイズによって採用することができないが、再利用可能な部品を作成したい場合に、WebComponentsを利用するとライブラリやフレームワークに依存せず作ることができることを知ったので試してみました。
Web Componentsとは
ライブラリを利用せず、HTML/CSS/JavaScriptだけで再利用可能なカプセル化されたHTMLタグを作成できる仕組みで、主に3つの要素(Custom element,Shadow Dom,HTML templates) で構成されいます。
※ HTML Imports も仕様として策定されているようなのですが、まだドラフト版のようなので、一旦入れていないです。
https://w3c.github.io/webcomponents/spec/imports/
構成要素
構成要素を一つ一つみていきます。
Custom element
独自の要素を生成する事ができる機能。
今まで独自のデザインを定義して再利用する場合は、classを作りその有無で設定していましたが、スタイルを含んだ独自要素を再利用可能になります。
作り方は、 HTMLElementを継承したクラスを作成して、customElements.defineのAPIを利用して以下のように定義します。
class MyBoldParagraph extends HTMLElement {
constructor() {
super();
this.innerHTML = `
<style>
p {
font-weight :bold;
}
</style>
<p>${this.innerHTML}</p>
`;
}
}
// 定義される独自タグの名前は**ハイフン**をつける必要が有ります。
customElements.define("bold-p",MyBoldParagraph);
また、HTMLElementにはライフサイクルがあり、documentに追加されたタイミングでコールバック関数を発火させることなどが可能です。
詳しくはこちら
Shadow Dom
コンポーネント内で独自にスコープを持つことができる機能。
スタイルを定義した時は、基本的に名前空間を持つことができないので、複数のcssファイルを読み込んだ際に、セレクタが重複した場合上書きが行われて、影響範囲を把握しにくく管理しにくい状態でしたが、 Shadow Domを利用すれば、コンポーネント内に影響範囲を抑えることができます。
先ほどの、Custom elementの例では、 pタグに再度スタイルを追加すると上書きされてしまうのですが、Shadow Domを利用する事で上書きされない状態を作ることができます。
使い方は、attachShadow APIを利用して以下のように定義します。
class MyBoldParagraph extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `
<style>
p {
font-weight :bold;
}
</style>
<p>${this.innerHTML}</p>
`;
}
}
customElements.define("bold-p",MyBoldParagraph);
HTML templates
再利用したいテンプレートをdocument上に配置し、使いまわすことができる機能。
templeteタグを利用して、document上に配置された要素は、Domにレンダリングされないが、javascriptから参照は可能になります。
利用方法は、templeteタグを以下のように定義します。
<template id="my-paragraph">
<p>My paragraph</p>
</template>
その後、以下のコードのようにJavaScript を用いて参照を取り DOM に追加するとページ上に表示できます。
let template = document.getElementById('my-paragraph');
let templateContent = template.content;
document.body.appendChild(templateContent);
Custom elementを利用する際に、HTMLタグやスタイルをjavascript側に記述する必要があるのですが、それをtempleteタグを利用することで、HTML側に記述して責務を分ける事が可能になると思うのですが、まだあまり有効な使い方が思いついていないです、、、
参考サイト
まとめ
WebComponentsの利用方法について学んでみて、今まで悩まされてきた名前空間の問題を解決するような方法が、ライブラリなしで実装可能になっていることがわかり素直に感動しました。
また、実際まだ公式にサポートされていないブラウザはあるものの、社内で使うようなブラウザ環境がある程度限定できる場合は、利用していく事でよりマークアップ側もより整理された状態にしていく事が可能になると感じたので、今後の動向も注目していきたいです。