はじめに
Web Componentsは再利用可能でカプセル化されたコンポーネントを作る機能です。
Web Componentsを実装するには3つの機能を利用する必要があります。それぞれ別記事にて紹介しましたので是非ご覧ください。
- Custom Elements コンポーネントの要素と動作を定義するための機能です。
- Shadow DOM カプセル化を担う機能です。
-
HTML Template 再利用を可能にする機能です。
この記事ではこれらの機能を組み合わせてWeb Componentsの実装を行います。
実装
例としてtier-table
という名前でtier表(ランキングボード)を作成する要素を作ります。細かな仕様は以下の通りです。
- 複数の対象をA、B、Cの三段階で分類して表示する
- 表示する内容は
tier-table
の中にli
を用いてslotでA、B、Cのどこに分類するか指定する - 表の背景色はA、B、Cのそれぞれ
tier-table
に属性で指定することができる
tier-table
を利用して実装したサンプルをまず紹介します。
<button id="random-color">背景色をランダムに変更する</button>
<tier-table id="tier-table" color-a="#f88" color-b="#fb8" color-c="#fd8">
<li slot="tier-a">find</li>
<li slot="tier-a">forEach</li>
<li slot="tier-a">map</li>
<li slot="tier-a">reduce</li>
<li slot="tier-a">filter</li>
<li slot="tier-b">concat</li>
<li slot="tier-b">some</li>
<li slot="tier-b">every</li>
<li slot="tier-b">fill</li>
<li slot="tier-b">pot</li>
<li slot="tier-b">push</li>
<li slot="tier-c">at</li>
<li slot="tier-c">of</li>
</tier-table>
const randomButton = document.getElementById('random-color');
randomButton.addEventListener('click', () => {
const tierTable = document.getElementById('tier-table');
tierTable.setAttribute('color-a', randomColor());
tierTable.setAttribute('color-b', randomColor());
tierTable.setAttribute('color-c', randomColor());
});
function randomColor() {
return "#" + Math.floor(Math.random() * 16777215).toString(16);
}
表示は以下のようになります。
小要素にリストを記述して属性を与えるだけで決まった表示を得ることができました。liの内容をフルーツの種類などに変えれば同じデザインの他の表にすることができますし、属性を変更することでデザインは変えずに背景色だけ変更した表を作ることができます。
利用例を見て作るもののイメージがついたところで実装の説明に入ります。template
は以下のようになっています。
<template id="tmp">
<div class="ul-a">
<p>A</p>
<ul>
<slot name="tier-a"></slot>
</ul>
</div>
<div class="ul-b">
<p>B</p>
<ul>
<slot name="tier-b"></slot>
</ul>
</div>
<div class="ul-c">
<p>C</p>
<ul>
<slot name="tier-c"></slot>
</ul>
</div>
</template>
A、B、Cごとにdiv
で分離してあります。p
はA、B、Cの段階の説明、ul
はslot
で受け取るリストをまとめるようなHTMLのテンプレートを作成しました。
次にtier-table
の機能の実装です。
class TierTable extends HTMLElement {
constructor() {
super();
this.attachShadow({
mode: 'open'
}).appendChild(document.getElementById('tmp').content.cloneNode(true));
this.setStyle();
}
static get observedAttributes() {
return ['color-a', 'color-b', 'color-c'];
}
attributeChangedCallback(){
this.setStyle();
}
setStyle() {
const style = document.createElement('style');
style.textContent = `
div { border: 1px solid }
.ul-a { background: ${this.getAttribute('color-a')} }
.ul-b { background: ${this.getAttribute('color-b')} }
.ul-c { background: ${this.getAttribute('color-c')} }
`;
this.shadowRoot.appendChild(style);
}
}
customElements.define('tier-table', TierTable);
constructor
ではtier-table
をattachShadow
によってShadow DOM化を行い、そこに先ほど作成したtemplate
の中身をクローンしたものを割り当てています。最後に呼び出しているsetStyle
はその時の属性をstyleに割り当てるものです。
さらにtier-table
では属性の変更によってstyleを変更する必要があるのでobservedAttributes
で属性を追跡してattributeChangedCallback
で属性が変化したタイミングでsetStyle
を呼び出してstyleを最新のものへ変更しています。
最後にそのクラスをCustomElementRegistry.define
で登録して完了です。
これで実装は完了です。それぞれの機能を把握した後に行うととても簡単に作ることができました。tier-table
は読み込んだファイルであれば何度でもhtmlの要素のように呼び出すことが可能ですし、呼び出された中身もShadow DOMによって隠蔽(カプセル化)されました。これでWeb Componentsの作成は完了です。
サンプルの全貌は以下のCodePenを参照して下さい。
See the Pen Web Components by KokiSakano (@kokisakano) on CodePen.
UIライブラリとの比較
ReactやVueなどのUIライブラリを利用している方はWeb Componentsをわざわざ作る利点がわからなかったと思います。Web ComponentsはUIライブラリに依存していないのでどんな環境であってもコンポーネントとして利用することができるという利点があります(カプセル化されているというメリットもあります)。逆にUIライブラリではデータとDOMの同期に対して高い能力を持っており、状態管理の機能と豊富なエコシステムが整っている利点があります。どちらも一長一短ですので、それぞれに合わせたユースケースで利用して下さい。
まとめ
Web Componentsの実装を行いました。UIライブラリでは実現できなかった機能なども実現できるので状況に合わせて使ってみてはいかがでしょうか。