template を利用する
template の使い方
基本的な使い方は MDN に載っているので、コレをまず参考にする。
これをもとに、index.html
をこんな感じ
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> span { color: red; } </style> <script src="./EchoTag.js"></script> </head> <body> <template id="simpleTemplate"> <div style="border:1px solid red;border-radius: .5rem;padding: 1rem;display: inline-block;"> <slot name="paragraph">Default paragraph.</slot> </div> </template> <echo-tag> <p slot="paragraph">Welcome to template!</p> </echo-tag> </body> </html>
EchoTag.js
をこんな感じに記載する
class EchoTag extends HTMLElement { // コンストラクタ constructor() { super(); this._innerText = null; this.attachShadow({mode: 'open'}); } // このタグが親のタグに配置されると呼び出されるコールバック connectedCallback() { this._updateRendering(); } // レンダリングしてみる。 _updateRendering() { this.shadowRoot.innerHTML = null; const template = document.querySelector('#simpleTemplate'); this.shadowRoot.appendChild(template.content.cloneNode(true)); } } // カスタム要素として、ブラウザに登録する customElements.define("echo-tag", EchoTag);
結果はこんな感じ。
template
タグ自体は実際にはレンダリングされていないくて、JavaScript 側で テンプレートの取得、中身のクローンを作って表示します。
それと同時に、slot
が評価されて、内容の埋込みが行われています。
template を親の HTML から独立させたい!
Web Component と言ってるのに、template
が親に含まれていてはコンポーネントとして微妙に思えてしまいます。
そこで、どうにかできないか考えてみました。
JavaScript に突っ込んじゃえ
index.html
側から template
タグをまるっと削除して
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> span { color: red; } </style> <script src="./EchoTag.js"></script> </head> <body> <echo-tag> <p slot="paragraph">Welcome to template!</p> </echo-tag> </body> </html>
代わりに JavaScript 側で
class EchoTag extends HTMLElement { // コンストラクタ constructor() { super(); this._innerText = null; this.attachShadow({mode: 'open'}); } // このタグが親のタグに配置されると呼び出されるコールバック connectedCallback() { this._updateRendering(); } template() { const template = document.createElement('template'); template.innerHTML = '<div style="border:1px solid red;border-radius: .5rem;padding: 1rem;display: inline-block;"><slot name="paragraph">HAHAHA</slot></div>' return template; } // レンダリングしてみる。 _updateRendering() { this.shadowRoot.innerHTML = null; const template = document.querySelector('#simpleTemplate'); this.shadowRoot.appendChild(this.template().content.cloneNode(true)); } } // カスタム要素として、ブラウザに登録する customElements.define("echo-tag", EchoTag);
これやっても良いのですけど、JSX が欲しくなりますね…
template を別ファイルに切り出しちゃえ
ということで、template.html
を作成して
<style> div { border:1px solid red; border-radius: .5rem; padding: 1rem; display: inline-block; } </style> <div> <slot name="paragraph"></slot> </div>
当然 index.html
に template
はなく
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> span { color: red; } </style> <script src="./EchoTag.js"></script> </head> <body> <echo-tag> <p slot="paragraph">Welcome to template!</p> </echo-tag> </body> </html>
EchoTag.js
の中で、template.html
を読み込んで処理します。
class EchoTag extends HTMLElement { // コンストラクタ constructor() { super(); this._innerText = null; this.attachShadow({mode: 'open'}); } // このタグが親のタグに配置されると呼び出されるコールバック connectedCallback() { this._updateRendering(); } async template() { return fetch('./template.html') .then(response => { return response.text() }) .then(innerHtml => { const templateTag = document.createElement('template'); templateTag.innerHTML = innerHtml; return templateTag; }); } // レンダリングしてみる。 async _updateRendering() { this.shadowRoot.appendChild((await this.template()).content.cloneNode(true)); } } // カスタム要素として、ブラウザに登録する customElements.define("echo-tag", EchoTag);
だいぶマシになったかな?