技術をかじる猫

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

純粋な HTML+JavaScript だけでカスタムタグを作る

カスタムなタグを作成してコンポーネントを作成するフレームワークを使ったことのある人は多いと思う。
と言うかこのサイトで紹介している LWC なんてまんまそれだし…

white-azalea.hatenablog.jp

で、この タグを作るって仕様的にどうなってるん?

て事で調べてみた。

developer.mozilla.org

蛇足が多い…!?

でもなんとか成功したのでその作り方をメモする。

タグを作成して宣言する

ここではあくまでタグを宣言して使うことだけを考える。
なので、仕様は最小限で

<echo-tag message="HAHAHA!"></echo-tag>

こんな感じで書いたら、「HAHAHA」と文字列を出力するだけのタグを考える。
まずは、適当に EchoTag.js ファイルを作成して以下のように書く

class EchoTag extends HTMLElement {

    // コンストラクタ
    constructor() {
        super();
        this._innerText = null;
    }
  
    // この変数に宣言された属性は、追加/削除/更新されたときに attributeChangedCallback が呼ばれる
    static observedAttributes = ["message"];
  
    // 属性値が更新されたら呼び出されるコールバック。
    attributeChangedCallback(name, oldValue, newValue) {
        this._innerText = newValue;
        this._updateRendering();
    }

    // このタグが親のタグに配置されると呼び出されるコールバック
    connectedCallback() {
        this._updateRendering();
    }
  
    // JavaScript でタグ作成したときの属性パラメータ
    get message() {
        return this._innerText;
    }
    set message(v) {
        this.setAttribute("message", v);
    }
  
    // カスタム関数。ココでは Shadow DOM を宣言して、span タグを作って表示するだけ。
    _updateRendering() {
        this.attachShadow({mode: 'open'});

        // append child.
        const spanElement = document.createElement("span");
        spanElement.innerHTML = this._innerText;

        this.shadowRoot.append(spanElement);
    }
}

// カスタム要素として、ブラウザに登録する
customElements.define("echo-tag", EchoTag);

タグとして利用する

使う側の HTML は 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>

    <!-- タグ定義を読み込んで -->
    <script src="./EchoTag.js"></script>
</head>
<body>
    <div><span>Example!</span></div>

    <!-- 使うだけ -->
    <echo-tag message="Welcome to custom tag!"></echo-tag>
</body>
</html>

動かしてみる

非常にシンプルで、書くだけ書いたらサーバを建てよう。
自分はめんどくさいので Python 3.11.x で python -m http.server 8000 でサーバを立てた。

その結果はこの通り

因みに、Shadow DOM を使わない場合は、_updateRendering を次の様に書く。

    _updateRendering() {
        // this.attachShadow({mode: 'open'});

        this.innerHTML = null;

        // append child.
        const spanElement = document.createElement("span");
        spanElement.innerHTML = this._innerText;
        this.append(spanElement);

        // this.shadowRoot.append(spanElement);
    }

結果が若干異なる。

Shadow DOM に関しては明日調査する…