技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

template を利用する

template の使い方

基本的な使い方は MDN に載っているので、コレをまず参考にする。

developer.mozilla.org

これをもとに、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 が欲しくなりますね…

react.dev

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.htmltemplate はなく

<!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);

だいぶマシになったかな?