2
3

More than 1 year has passed since last update.

ES2022を使ってCustom Elementを書く

Posted at

ES2022でクラス周りの構文が強化されたため、Custom Elementが書きやすくなっています。

class private field

クラスにprivateフィールドを作成できる機能です。

この機能は、Custom Element内でShadow DOMを使う時に効果を発揮します。
Shadow DOMはelement.attachShadowという関数を使用して作成するのですが、生成したshadow DOMへの参照を隠蔽するのは従来は困難でした。

従来
class FooElement extends HTMLElement {
  connectedCallback() {
    // shadow DOMを隠蔽するには `mode: "closed"` にする
    this._shadow = this.attachShadow({ mode: "closed" });
    this._shadow.appendChild(document.createElement("div"));
  }
}

// `mode: "closed"`にしたにもかかわらず、shadow domにアクセスできてしまう
new FooElement()._shadow

アンダースコア始まりの疑似privateを使っている場合、クラスの外部からもアクセスできてしまうため、shadow DOMへの参照は隠蔽できません。
もし完全にshadow DOMへの参照を隠蔽したい場合は、全体を即時関数で囲ったうえでWeakMapに参照を保存する等の努力が必要でした。そのため、closed modeでshadow DOMを作成する価値はないという意見もありました(参考:こちらのブログ記事(英語))。

しかし、ES2022で導入されたprivateフィールドを使うと、shadow DOMへの参照を外部から隠蔽することができます。

ES2022
class FooElement extends HTMLElement {
  #shadow // private fieldを使ってshadow DOMへの参照を保持
  connectedCallback() {
    this.#shadow = this.attachShadow({ mode: "closed" });
    this.#shadow.appendChild(document.createElement("div"));
  }
}

new FooElement().#shadow // エラー(shadow domにアクセスできない)

このように、privateフィールドとshadow DOMを組み合わせることで、custom elementのより強いカプセル化が可能になりました。

class static block

custom elementを作成するには、作成したcustom element用クラスをcustomElement.define関数で登録する必要があります。

customElement.defineの使い方(従来)
// custom element用クラスの定義
class FooElement extends HTMLElement {
  // 省略
}

// custom element用クラスを登録
customElements.define("foo-element", FooElement);

これまでは上のように、customElements.defineはクラスの外側に書くしかありませんでしたが、ES2022で導入されたclass static blockを使うとクラスの内側に書くことができます

customElement.define+class-static-block
// custom element用クラスの定義
class FooElement extends HTMLElement {
  static {
    customElements.define("foo-element", this);
  }
  // 省略
}

static {}という部分の中身が、クラスの初期化時に実行されるため、customElements.defineの実行に使えるというわけです。

まとめ

  • Custom Elementには普通のプロパティやメソッドだけでなく、privateなプロパティやメソッドも設定できる
    • privateプロパティにshadow DOMへの参照を保存すると、外部からshadow DOMへのアクセスを遮断できる(※)
  • class static block内でcustomElements.defineを実行できる

(※)ちなみに、private propertyにすれば完全にshadow DOMへのアクセスを防げるというわけではなく、以下のようなコードを挿入されると外部からshadow DOMへアクセスすることが可能になってしまいます。

const originalAttachShadow = Element.prototype.attachShadow;
Element.prototype.attachShadow = function() {
  return originalAttachShadow.call(this, { mode: "open" });
};

なので、完全なprivate化ができるわけではないという事に注意してください。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3