1. 4.13 カスタム要素
      1. 4.13.1 導入
        1. 4.13.1.1 自律カスタム要素の作成
        2. 4.13.1.2 フォームに関連付けられたカスタム要素の作成
        3. 4.13.1.3 デフォルトでアクセシブルなロール、ステート、およびプロパティをもつカスタム要素を作成する
        4. 4.13.1.4 カスタマイズされた組み込み要素の作成
        5. 4.13.1.5 自律カスタム要素の欠点
        6. 4.13.1.6 作成後の要素のアップグレード
        7. 4.13.1.7 Exposing custom element states
      2. 4.13.2 カスタム要素のコンストラクターと反応の要件
      3. 4.13.3 コアコンセプト
      4. 4.13.4 CustomElementRegistryインターフェイス
      5. 4.13.5 Upgrades
      6. 4.13.6 カスタム要素応答
      7. 4.13.7 要素内部
        1. 4.13.7.1 The ElementInternals interface
        2. 4.13.7.2 シャドウルートアクセス
        3. 4.13.7.3 フォームに関連付けられたカスタム要素
        4. 4.13.7.4 アクセシビリティセマンティックス
        5. 4.13.7.5 Custom state pseudo-class

4.13 カスタム要素

Using_custom_elements

Support in all current engines.

Firefox63+Safari10.1+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

4.13.1 導入

この節は非規範的である。

カスタム要素は、著者にフル機能のDOM要素を独自に構築する方法を提供する。スクリプティングなどで後に追加されたアプリケーション固有の動作を用いて、著者は文書内で非標準要素を常に使用することができるが、そのような要素は歴史的に不適合であり、あまり機能的ではなかった。カスタム要素を定義することで、著者はパーサーに要素を適切に構築する方法、およびそのクラスの要素が変更にどのように反応すべきかを伝えることができる。

カスタム要素は、(カスタム要素の定義のような)低レベルの著者に公開される拡張ポイントの言葉で、(HTMLの要素のような)既存のプラットフォームの機能を説明することによって、"プラットフォームを合理的に説明し"ようとするより大きな活動の一部である。現在、カスタム要素の能力には、機能的にも意味的にも多くの制限があり、HTMLの既存の要素の振る舞いを完全に説明することを妨げているが、我々は時が経つにつれこの隔たりが縮小することを望む。

4.13.1.1 自律カスタム要素の作成

この節は非規範的である。

自律カスタム要素を作成する方法を説明するために、国旗の小さなアイコンのレンダリングをカプセル化するカスタム要素を定義してみよう。私たちの目標は、次のように使用できるようにすることである:

<flag-icon country="nl"></flag-icon>

これを行うには、まずカスタム要素のクラスを宣言し、HTMLElementを拡張する:

class FlagIcon extends HTMLElement {
  constructor() {
    super();
    this._countryCode = null;
  }

  static observedAttributes = ["country"];

  attributeChangedCallback(name, oldValue, newValue) {
    // name will always be "country" due to observedAttributes
    this._countryCode = newValue;
    this._updateRendering();
  }
  connectedCallback() {
    this._updateRendering();
  }

  get country() {
    return this._countryCode;
  }
  set country(v) {
    this.setAttribute("country", v);
  }

  _updateRendering() {
    // Left as an exercise for the reader. But, you'll probably want to
    // check this.ownerDocument.defaultView to see if we've been
    // inserted into a document with a browsing context, and avoid
    // doing any work if not.
  }
}

次に、要素を定義するために次のクラスを使用して必要がある:

customElements.define("flag-icon", FlagIcon);

この時点で、上記のコードは動作する。flag-iconタグを見るたびに、パーサーはFlagIconクラスの新しいインスタンスを作成し、要素の内部状態を設定し、レンダリングを更新するために使用する(適切な場合)、新しいcountry属性についてコードに伝える。

DOM APIを使用してflag-icon要素を作成することもできる:

const flagIcon = document.createElement("flag-icon")
flagIcon.country = "jp"
document.body.appendChild(flagIcon)

最後に、カスタム要素コンストラクター自体を使用することもできる。つまり、上記のコードは次のコードと同じである:

const flagIcon = new FlagIcon()
flagIcon.country = "jp"
document.body.appendChild(flagIcon)
4.13.1.2 フォームに関連付けられたカスタム要素の作成

この節は非規範的である。

trueの値を持つ静的なformAssociatedプロパティを追加すると、自律カスタム要素フォーム関連カスタム要素になる。ElementInternalsインターフェイスは、フォームコントロール要素に共通の関数およびプロパティを実装するのに役立つ。

class MyCheckbox extends HTMLElement {
  static formAssociated = true;
  static observedAttributes = ['checked'];

  constructor() {
    super();
    this._internals = this.attachInternals();
    this.addEventListener('click', this._onClick.bind(this));
  }

  get form() { return this._internals.form; }
  get name() { return this.getAttribute('name'); }
  get type() { return this.localName; }

  get checked() { return this.hasAttribute('checked'); }
  set checked(flag) { this.toggleAttribute('checked', Boolean(flag)); }

  attributeChangedCallback(name, oldValue, newValue) {
    // name will always be "checked" due to observedAttributes
    this._internals.setFormValue(this.checked ? 'on' : null);
  }

  _onClick(event) {
    this.checked = !this.checked;
  }
}
customElements.define('my-checkbox', MyCheckbox);

カスタム要素my-checkboxは、組み込みのフォームに関連付けられた要素のように使うことができる。たとえば、formまたはlabel の中に置くことでmy-checkbox要素が関連付けられ、formを送信することでmy-checkbox実装で提供されるデータが送信される。

<form action="..." method="...">
  <label><my-checkbox name="agreed"></my-checkbox> I read the agreement.</label>
  <input type="submit">
</form>
4.13.1.3 デフォルトでアクセシブルなロール、ステート、およびプロパティをもつカスタム要素を作成する

この節は非規範的である。

ElementInternalsの適切なプロパティを使用することにより、カスタム要素はデフォルトのアクセシビリティセマンティックスを持つことができる。 次のコードは、前のセクションのフォームに関連付けられたチェックボックスを拡張して、アクセシビリティ技術から見たデフォルトのロールおよびチェック状態を適切に設定する:

class MyCheckbox extends HTMLElement {
  static formAssociated = true;
  static observedAttributes = ['checked'];

  constructor() {
    super();
    this._internals = this.attachInternals();
    this.addEventListener('click', this._onClick.bind(this));

    this._internals.role = 'checkbox';
    this._internals.ariaChecked = 'false';
  }

  get form() { return this._internals.form; }
  get name() { return this.getAttribute('name'); }
  get type() { return this.localName; }

  get checked() { return this.hasAttribute('checked'); }
  set checked(flag) { this.toggleAttribute('checked', Boolean(flag)); }

  attributeChangedCallback(name, oldValue, newValue) {
    // name will always be "checked" due to observedAttributes
    this._internals.setFormValue(this.checked ? 'on' : null);
    this._internals.ariaChecked = this.checked;
  }

  _onClick(event) {
    this.checked = !this.checked;
  }
}
customElements.define('my-checkbox', MyCheckbox);

組み込み要素の場合と同様に、これらはデフォルトにすぎず、roleおよびaria-*属性を使用するページ著者によって上書きできることに注意する:

<!-- This markup is non-conforming -->
<input type="checkbox" checked role="button" aria-checked="false">
<!-- This markup is probably not what the custom element author intended -->
<my-checkbox role="button" checked aria-checked="false">

カスタム要素の著者は、アクセシビリティセマンティックスのどの側面が強いネイティヴセマンティックスであるかを説明することが勧められる。つまり、カスタム要素のユーザーによって上書きされるべきではない。この例では、my-checkbox要素の著者は、そのroleおよびaria-checked値は強いネイティヴセマンティックスであるため、上記のようなコードを勧めないと述べている。

4.13.1.4 カスタマイズされた組み込み要素の作成

この節は非規範的である。

カスタマイズされた組み込み要素は、自律カスタム要素とはわずかに異なる定義があり、そして大きく異なる使われ方をする、独自の種類のカスタム要素である。この要素は、新しいカスタム機能でHTMLの既存要素を拡張することで、HTMLの既存要素由来の動作の再利用を可能にするために存在する。残念なことに、HTML要素の既存の動作の多くが自律カスタム要素を純粋に使用して複製できないため、これは重要である。代わりに、カスタマイズされた組み込み要素は、既存の要素にカスタム構築動作、ライフサイクルフック、およびプロトタイプチェーンのインストールを可能にし、既存の要素の上にこれらの機能を本質的に"混在"させる。

カスタマイズされた組み込み要素は、ユーザーエージェントや他のソフトウェアが要素のセマンティックスおよび動作を識別するために要素のローカル名として扱うので、自律カスタム要素とは異なる構文を必要とする。つまり、既存の動作の上に構築するカスタマイズされた組み込み要素の概念は、元のローカル名を保持する拡張要素に決定的に依存する。

この例において、plastic-buttonというカスタマイズされた組み込み要素を作成する。これは通常のボタンのように動作するが、クリックするたびにファンシーなアニメーション効果が追加される。今回はHTMLElementの代わりにHTMLButtonElementを拡張するが、前と同じようにクラスを定義することから始める:

class PlasticButton extends HTMLButtonElement {
  constructor() {
    super();

    this.addEventListener("click", () => {
      // Draw some fancy animation effects!
    });
  }
}

カスタム要素を定義する場合、extendsオプションも指定する必要がある:

customElements.define("plastic-button", PlasticButton, { extends: "button" });

一般に、拡張されている要素の名前は、同じインターフェイスを共有する要素(HTMLQuoteElementを共有するqblockquoteの両者など)を共有するため、拡張する要素インターフェイスを単に調べるだけでは判断することができない。

解析されたHTMLソーステキストからカスタマイズされた組み込み要素を構築するには、button要素のis属性を使用する。

<button is="plastic-button">Click Me!</button>

カスタマイズされた組み込み要素自律カスタム要素として使用しようとするとうまくいかない。つまり、<plastic-button>Click me?</plastic-button>は特別な動作をしないHTMLElementを作成するだけである。

プログラムでカスタマイズしたビルトイン要素を作成する必要がある場合、次のcreateElement()の形式を使用することができる:

const plasticButton = document.createElement("button", { is: "plastic-button" });
plasticButton.textContent = "Click me!";

前と同じように、コンストラクターも動作する:

const plasticButton2 = new PlasticButton();
console.log(plasticButton2.localName);  // will output "button"
console.assert(plasticButton2 instanceof PlasticButton);
console.assert(plasticButton2 instanceof HTMLButtonElement);

プログラムでカスタマイズした組み込み要素を作成する場合、明示的に設定されていないのでis属性はDOMに存在しない。ただし、シリアライズ時に出力に追加される

console.assert(!plasticButton.hasAttribute("is"));
console.log(plasticButton.outerHTML); // will output '<button is="plastic-button"></button>'

作成方法に関係なく、buttonが特別であるすべての方法は、そのような"プラスチックボタン"にも適用される:フォーカス動作、フォーム送信に関与する能力、disabled属性など。

カスタマイズされた組み込み要素は、有用なユーザーエージェントが提供する動作またはAPIを持つ既存のHTML要素の拡張を可能にするように設計されている。そのため、この仕様で定義される既存のHTML要素のみを拡張することができ、要素インターフェイスとしてHTMLUnknownElementを使用するように定義されたbgsoundblinkisindexkeygenmulticolnextid、またはspacerなどのレガシー要素は拡張できない。

この要件の1つの理由は将来の互換性である。もしカスタマイズされた組み込み要素が、たとえばcomboboxのような、現在不明の要素を拡張したものが定義されたなら、派生したカスタマイズされた組み込み要素のコンシューマーが、ユーザーエージェント提供の動作に興味を持たない基本要素に依存するようになるため、これはこの仕様が将来combobox要素を定義することを妨げるかもしれない。

4.13.1.5 自律カスタム要素の欠点

この節は非規範的である。

以下で規定されているように、そして上記で触れたように、単にtaco-buttonという要素を定義して使用しても、そのような要素がボタンを表すことを意味しない。つまり、ウェブブラウザー、検索エンジン、アクセシビリティ技術などのツールは、定義された名前だけに基づいて、結果の要素をボタンとして自動的に処理しない。

自律カスタム要素を依然として使用する一方で、様々なユーザーに所望のボタンセマンティックスを伝えるためためには、複数の技術を用いる必要がある:

これらの点を念頭に置いて、(無効にする機能を含む)ボタンのセマンティックスを伝える責任を負ったフル機能のtaco-buttonは、次のようになる:

class TacoButton extends HTMLElement {
  static observedAttributes = ["disabled"];

  constructor() {
    super();
    this._internals = this.attachInternals();
    this._internals.role = "button";

    this.addEventListener("keydown", e => {
      if (e.code === "Enter" || e.code === "Space") {
        this.dispatchEvent(new PointerEvent("click", {
          bubbles: true,
          cancelable: true
        }));
      }
    });

    this.addEventListener("click", e => {
      if (this.disabled) {
        e.preventDefault();
        e.stopImmediatePropagation();
      }
    });

    this._observer = new MutationObserver(() => {
      this._internals.ariaLabel = this.textContent;
    });
  }

  connectedCallback() {
    this.setAttribute("tabindex", "0");

    this._observer.observe(this, {
      childList: true,
      characterData: true,
      subtree: true
    });
  }

  disconnectedCallback() {
    this._observer.disconnect();
  }

  get disabled() {
    return this.hasAttribute("disabled");
  }
  set disabled(flag) {
    this.toggleAttribute("disabled", Boolean(flag));
  }

  attributeChangedCallback(name, oldValue, newValue) {
    // name will always be "disabled" due to observedAttributes
    if (this.disabled) {
      this.removeAttribute("tabindex");
      this._internals.ariaDisabled = "true";
    } else {
      this.setAttribute("tabindex", "0");
      this._internals.ariaDisabled = "false";
    }
  }
}

このかなり複雑な要素定義であっても、要素はカスタマーに使用する喜びとはならない。その要素は、自身の意志のtabindex属性を継続的に"発芽"し、tabindex="0"フォーカス機能の動作の選択は、現在のプラットフォームのbutton動作と一致しなくてもよい 。これは、(通常、コンシューマーがデフォルトの動作を上書きできるように予約されているにもかかわらず)そのためにtabindex属性の使用を強制する、現時点では、カスタム要素のデフォルトのフォーカス動作を指定する方法がないためである。

対照的に、前節で示したように、単一のカスタマイズされた組み込み要素は、button要素のセマンティックスと動作を自動的に継承する。これらの動作を手動で実装する必要はない。一般に、HTMLの既存要素の上に構築される重要な動作やセマンティックスを持つ要素については、カスタマイズされた組み込み要素の開発、保守、および消費が容易になる。

4.13.1.6 作成後の要素のアップグレード

この節は非規範的である。

要素定義はいつでも発生する可能性があるため、非カスタム要素が作成され、適切な定義を登録した後でカスタム要素にすることができる。 このプロセスを、通常の要素からカスタム要素に要素を"アップグレードする"と呼ぶ。

アップグレードは、パーサーなどで関連する要素が最初に作成された後に、カスタム要素定義を登録することが望ましい場合のシナリオを可能にする。これにより、カスタム要素内のコンテンツを段階的に強化することができる。例えば、以下の HTML文書において、img-viewerの要素定義は非同期的にロードされる:

<!DOCTYPE html>
<html lang="en">
<title>Image viewer example</title>

<img-viewer filter="Kelvin">
  <img src="images/tree.jpg" alt="A beautiful tree towering over an empty savannah">
</img-viewer>

<script src="js/elements/img-viewer.js" async></script>

ここでのimg-viewer要素の定義は、マークアップで<img-viewer>タグの後に配置された、async属性でマークされたscript要素を用いて読み込まれる。 スクリプトの読み込み中、img-viewer要素は、spanと同様に未定義の要素として扱われる。 スクリプトが読み込まれると、img-viewer要素が定義され、ページ上の既存のimg-viewer要素は、カスタム要素の定義が適用してアップグレードされる(これはおそらく、文字列"Kelvin"によって識別される画像フィルターの適用し、画像の視覚的外観を向上させることが含まれる )。


アップグレードは文書ツリー内の要素にのみ適用されることに注意する。(正式には、接続される要素。)文書に挿入されない要素は、アップグレードされないままである。例はこの点を説明する:

<!DOCTYPE html>
<html lang="en">
<title>Upgrade edge-cases example</title>

<example-element></example-element>

<script>
  "use strict";

  const inDocument = document.querySelector("example-element");
  const outOfDocument = document.createElement("example-element");

  // Before the element definition, both are HTMLElement:
  console.assert(inDocument instanceof HTMLElement);
  console.assert(outOfDocument instanceof HTMLElement);

  class ExampleElement extends HTMLElement {}
  customElements.define("example-element", ExampleElement);

  // After element definition, the in-document element was upgraded:
  console.assert(inDocument instanceof ExampleElement);
  console.assert(!(outOfDocument instanceof ExampleElement));

  document.body.appendChild(outOfDocument);

  // Now that we've moved the element into the document, it too was upgraded:
  console.assert(outOfDocument instanceof ExampleElement);
</script>
4.13.1.7 Exposing custom element states

Built-in elements provided by user agents have certain states that can change over time depending on user interaction and other factors, and are exposed to web authors through pseudo-classes. For example, some form controls have the "invalid" state, which is exposed through the :invalid pseudo-class.

Like built-in elements, custom elements can have various states to be in too, and custom element authors want to expose these states in a similar fashion as the built-in elements.

This is done via the :state() pseudo-class. A custom element author can use the states property of ElementInternals to add and remove such custom states, which are then exposed as arguments to the :state() pseudo-class.

The following shows how :state() can be used to style a custom checkbox element. Assume that LabeledCheckbox doesn't expose its "checked" state via a content attribute.

<script>
class LabeledCheckbox extends HTMLElement {
  constructor() {
    super();
    this._internals = this.attachInternals();
    this.addEventListener('click', this._onClick.bind(this));

    const shadowRoot = this.attachShadow({mode: 'closed'});
    shadowRoot.innerHTML =
      `<style>
       :host::before {
         content: '[ ]';
         white-space: pre;
         font-family: monospace;
       }
       :host(:state(checked))::before { content: '[x]' }
       </style>
       <slot>Label</slot>`;
  }

  get checked() { return this._internals.states.has('checked'); }

  set checked(flag) {
    if (flag)
      this._internals.states.add('checked');
    else
      this._internals.states.delete('checked');
  }

  _onClick(event) {
    this.checked = !this.checked;
  }
}

customElements.define('labeled-checkbox', LabeledCheckbox);
</script>

<style>
labeled-checkbox { border: dashed red; }
labeled-checkbox:state(checked) { border: solid; }
</style>

<labeled-checkbox>You need to check this</labeled-checkbox>

Custom pseudo-classes can even target shadow parts. An extension of the above example shows this:

<script>
class QuestionBox extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({mode: 'closed'});
    shadowRoot.innerHTML =
      `<div><slot>Question</slot></div>
       <labeled-checkbox part='checkbox'>Yes</labeled-checkbox>`;
  }
}
customElements.define('question-box', QuestionBox);
</script>

<style>
question-box::part(checkbox) { color: red; }
question-box::part(checkbox):state(checked) { color: green; }
</style>

<question-box>Continue?</question-box>

4.13.2 カスタム要素のコンストラクターと反応の要件

カスタム要素コンストラクターをオーサリングするとき、著者は以下の適合性要件に拘束される:

これらの要件のいくつかは、要素の作成中に直接的または間接的のいずれかでチェックされ、それらに従わないと、パーサーまたはDOM APIによってインスタンス化できないカスタム要素をもたらす。これは、たとえコンストラクターによって開始されたマイクロタスク内で作業が行われるとしても当てはまる。マイクロタスクのチェックポイントは、作成直後に発生する可能性があるためである。

カスタム要素反応を作成する場合、予期しない結果が生じる可能性があるため、ノードツリーの操作を避けるべきである。

要素のconnectedCallbackは、要素が切断される前にキューに入れることができるが、コールバックキューはまだ処理されているため、接続されなくなった要素のconnectedCallbackをもたらす:

class CParent extends HTMLElement {
  connectedCallback() {
    this.firstChild.remove();
  }
}
customElements.define("c-parent", CParent);

class CChild extends HTMLElement {
  connectedCallback() {
    console.log("CChild connectedCallback: isConnected =", this.isConnected);
  }
}
customElements.define("c-child", CChild);

const parent = new CParent(),
      child = new CChild();
parent.append(child);
document.body.append(parent);

// Logs:
// CChild connectedCallback: isConnected = false

4.13.3 コアコンセプト

カスタム要素は、カスタムである要素となる。非公式には、そのコンストラクターおよびプロトタイプが、ユーザーエージェントによってではなく、著者によって定義されていることを意味する。この著者が提供するコンストラクター関数をカスタム要素コンストラクターと呼ぶ。

2つの異なるタイプのカスタム要素を定義できる:

Global_attributes/is

Firefox63+SafariNoChrome67+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
  1. 自律カスタム要素。これは、extendsオプションなしで定義されている。 これらのタイプのカスタム要素は、定義された名前と同じローカル名を持つ。

  2. カスタマイズされた組み込み要素。これは、extendsオプションで定義されている。これらのタイプのカスタム要素は、extendsオプションで渡される値と同じローカル名を持ち、定義された名前is属性の値として使用され、したがってこれは妥当なカスタム要素名でなければならない。

カスタム要素作成された後、is属性の値を変更しても、isとして要素に保存されるため、要素の動作は変更されない。

自律カスタム要素は、以下の要素定義を持つ:

カテゴリー
フローコンテンツ
フレージングコンテンツ
パルパブルコンテンツ
フォームに関連付けられたカスタム要素の場合:記載ラベル付け可能送信可能リセット可能フォームに関連付けられた要素
この要素を使用できるコンテキスト
フレージングコンテンツが期待される場所。
コンテンツモデル
透過的
コンテンツ属性
グローバル属性is属性を除く
formフォームに関連付けられたカスタム要素の場合 — 要素をform要素に関連付ける
disabledフォームに関連付けられたカスタム要素の場合 — フォームコントロールが無効かどうか
readonlyフォームに関連付けられたカスタム要素の場合 — willValidateに影響し、カスタム要素の著者によって追加されたすべての動作
nameフォームに関連付けられたカスタム要素の場合 — フォームの送信およびform.elementsに使用する要素の名前
名前空間を持たないその他の属性(文参照)。
アクセシビリティの考慮
フォームに関連付けられたカスタム要素の場合:著者向け 実装者向け
そうでなければ:著者向け実装者向け
DOMインターフェイス
要素の著者によって提供される(HTMLElementから継承)

自律カスタム要素は特別な意味を持たない:その要素の子を表すカスタマイズされた組み込み要素は、それが拡張した要素のセマンティックスを継承する。

要素の著者によって決定された、要素の機能に関連する名前空間なしの属性は、属性名がXML互換、かつASCII大文字を含まない限り、自律カスタム要素上で指定してもよい。例外はis属性であり、これは自律カスタム要素に指定してはならない(かつ指定しても何の効果もない)。

カスタマイズされた組み込み要素は、それらが拡張する要素に基づいて、属性に関する通常の要件に従う。カスタム属性ベースの動作を追加するには、data-*属性を使用する。


フォーム関連フィールドがtrueに設定されているカスタム要素定義に要素が関連付けられている場合、自律カスタム要素フォームに関連付けられたカスタム要素と呼ばれる。

name属性は、フォームに関連付けられたカスタム要素の名前を表す。disabled属性は、フォームに関連付けられたカスタム要素を非対話的にし、その送信値が送信されないようにするために使用される。form属性は、フォームに関連付けられたカスタム要素をそのフォーム所有者に明示的に関連付けるために使用される。

フォームに関連付けられたカスタム要素readonly属性は、要素が制約検証から除外されることを指定する。ユーザーエージェントはこの属性のための他の動作を提供しないが、カスタム要素の著者は、可能であれば、組み込みフォームコントロール上のreadonly属性の動作と同様に、何らかの適切な方法でコントロールを編集不能にするために、その存在を使用すべきである。

制約検証フォームに関連付けられたカスタム要素readonly属性が指定されている場合、その要素は制約検証から除外される

フォームに関連付けられたカスタム要素リセットアルゴリズムは、要素、コールバック名"formResetCallback"、および空の引数リストをもつカスタム要素のコールバック反応をキューに加えることである。


妥当なカスタム要素名は、次のすべての要件を満たす一連の文字である:

これらの要件により、妥当なカスタム要素名の多数の目標が保証される。

これらの制限とは別に、<math-α><emotion-😍>などのユースケースに最大源の柔軟性を与えるために、さまざまな名前が許可されている。

A custom element definition describes a custom element and consists of:

A name
A valid custom element name
A local name
A local name
A constructor
A Web IDL CustomElementConstructor callback function type value wrapping the custom element constructor
A list of observed attributes
A sequence<DOMString>
A collection of lifecycle callbacks
A map, whose keys are the strings "connectedCallback", "disconnectedCallback", "adoptedCallback", "attributeChangedCallback", "formAssociatedCallback", "formDisabledCallback", "formResetCallback", and "formStateRestoreCallback". The corresponding values are either a Web IDL Function callback function type value, or null. By default the value of each entry is null.
A construction stack
A list, initially empty, that is manipulated by the upgrade an element algorithm and the HTML element constructors. Each entry in the list will be either an element or an already constructed marker.
A form-associated boolean
If this is true, user agent treats elements associated to this custom element definition as form-associated custom elements.
A disable internals boolean
Controls attachInternals().
A disable shadow boolean
Controls attachShadow().

To look up a custom element definition, given a document, namespace, localName, and is, perform the following steps. They will return either a custom element definition or null:

  1. If namespace is not the HTML namespace, return null.

  2. If document's browsing context is null, return null.

  3. Let registry be document's relevant global object's CustomElementRegistry object.

  4. If there is custom element definition in registry with name and local name both equal to localName, return that custom element definition.

  5. If there is a custom element definition in registry with name equal to is and local name equal to localName, return that custom element definition.

  6. Return null.

4.13.4 CustomElementRegistryインターフェイス

CustomElementRegistry

Support in all current engines.

Firefox63+Safari10.1+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

Each Window object is associated with a unique instance of a CustomElementRegistry object, allocated when the Window object is created.

カスタム要素コンストラクターHTMLElementインターフェイスを継承しており、Windowオブジェクトごとに正確に1 つのHTMLElementインターフェイスが存在するため、カスタム要素レジストリはDocument オブジェクトの代わりに、Windowオブジェクトに関連付けられる。

Window/customElements

Support in all current engines.

Firefox63+Safari10.1+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

The customElements attribute of the Window interface must return the CustomElementRegistry object for that Window object.

[Exposed=Window]
interface CustomElementRegistry {
  [CEReactions] undefined define(DOMString name, CustomElementConstructor constructor, optional ElementDefinitionOptions options = {});
  (CustomElementConstructor or undefined) get(DOMString name);
  DOMString? getName(CustomElementConstructor constructor);
  Promise<CustomElementConstructor> whenDefined(DOMString name);
  [CEReactions] undefined upgrade(Node root);
};

callback CustomElementConstructor = HTMLElement ();

dictionary ElementDefinitionOptions {
  DOMString extends;
};

Every CustomElementRegistry has a set of custom element definitions, initially empty. In general, algorithms in this specification look up elements in the registry by any of name, local name, or constructor.

Every CustomElementRegistry also has an element definition is running flag which is used to prevent reentrant invocations of element definition. It is initially unset.

Every CustomElementRegistry also has a when-defined promise map, mapping valid custom element names to promises. It is used to implement the whenDefined() method.

window.customElements.define(name, constructor)

CustomElementRegistry/define

Support in all current engines.

Firefox63+Safari10.1+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
指定された名前を指定されたコンストラクターに自律カスタム要素としてマッピングする、新しいカスタム要素を定義する。
window.customElements.define(name, constructor, { extends: baseLocalName })
指定された名前を指定されたコンストラクターに、提供されたbaseLocalNameで識別される要素タイプカスタマイズされた組み込み要素としてマッピングする、新しいカスタム要素を定義する。カスタム要素または不明な要素を拡張しようとすると、"NotSupportedError" DOMExceptionが投げられる。
window.customElements.get(name)

CustomElementRegistry/get

Support in all current engines.

Firefox63+Safari10.1+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
指定された名前に対して定義されたカスタム要素コンストラクターを回収する。指定された名前カスタム要素定義がない場合、undefinedを返す。
window.customElements.getName(constructor)
指定されたコンストラクターに対して定義されたカスタム要素の指定された名前を取得する。指定されたコンストラクターを持つカスタム要素定義がない場合、nullを返す。
window.customElements.whenDefined(name)

CustomElementRegistry/whenDefined

Support in all current engines.

Firefox63+Safari10.1+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
カスタム要素が指定された名前で定義されたときにカスタム要素のコンストラクターで満たされるプロミスを返す。 (そのようなカスタム要素がすでに定義されている場合、返されたプロミスはすぐに満たされる。)妥当なカスタム要素名が指定されない場合、 "SyntaxError" DOMExceptionで拒否されたプロミスを返す。
window.customElements.upgrade(root)

CustomElementRegistry/upgrade

Support in all current engines.

Firefox63+Safari12.1+Chrome68+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
たとえ接続されていないとしても、ルートシャドウを含むすべての子孫要素をアップグレードしようとする

Element definition is a process of adding a custom element definition to the CustomElementRegistry. This is accomplished by the define() method. When invoked, the define(name, constructor, options) method must run these steps:

  1. If IsConstructor(constructor) is false, then throw a TypeError.

  2. If name is not a valid custom element name, then throw a "SyntaxError" DOMException.

  3. If this CustomElementRegistry contains an entry with name name, then throw a "NotSupportedError" DOMException.

  4. If this CustomElementRegistry contains an entry with constructor constructor, then throw a "NotSupportedError" DOMException.

  5. Let localName be name.

  6. Let extends be the value of the extends member of options, or null if no such member exists.

  7. If extends is not null, then:

    1. If extends is a valid custom element name, then throw a "NotSupportedError" DOMException.

    2. If the element interface for extends and the HTML namespace is HTMLUnknownElement (e.g., if extends does not indicate an element definition in this specification), then throw a "NotSupportedError" DOMException.

    3. Set localName to extends.

  8. If this CustomElementRegistry's element definition is running flag is set, then throw a "NotSupportedError" DOMException.

  9. Set this CustomElementRegistry's element definition is running flag.

  10. Let formAssociated be false.

  11. Let disableInternals be false.

  12. Let disableShadow be false.

  13. Let observedAttributes be an empty sequence<DOMString>.

  14. Run the following substeps while catching any exceptions:

    1. Let prototype be ? Get(constructor, "prototype").

    2. If Type(prototype) is not Object, then throw a TypeError exception.

    3. Let lifecycleCallbacks be a map with the keys "connectedCallback", "disconnectedCallback", "adoptedCallback", and "attributeChangedCallback", each of which belongs to an entry whose value is null.

    4. For each of the keys callbackName in lifecycleCallbacks, in the order listed in the previous step:

      1. Let callbackValue be ? Get(prototype, callbackName).

      2. If callbackValue is not undefined, then set the value of the entry in lifecycleCallbacks with key callbackName to the result of converting callbackValue to the Web IDL Function callback type. Rethrow any exceptions from the conversion.

    5. If the value of the entry in lifecycleCallbacks with key "attributeChangedCallback" is not null, then:

      1. Let observedAttributesIterable be ? Get(constructor, "observedAttributes").

      2. If observedAttributesIterable is not undefined, then set observedAttributes to the result of converting observedAttributesIterable to a sequence<DOMString>. Rethrow any exceptions from the conversion.

    6. Let disabledFeatures be an empty sequence<DOMString>.

    7. Let disabledFeaturesIterable be ? Get(constructor, "disabledFeatures").

    8. If disabledFeaturesIterable is not undefined, then set disabledFeatures to the result of converting disabledFeaturesIterable to a sequence<DOMString>. Rethrow any exceptions from the conversion.

    9. Set disableInternals to true if disabledFeatures contains "internals".

    10. Set disableShadow to true if disabledFeatures contains "shadow".

    11. Let formAssociatedValue be ? Get( constructor, "formAssociated").

    12. Set formAssociated to the result of converting formAssociatedValue to a boolean. Rethrow any exceptions from the conversion.

    13. If formAssociated is true, for each of "formAssociatedCallback", "formResetCallback", "formDisabledCallback", and "formStateRestoreCallback" callbackName:

      1. Let callbackValue be ? Get(prototype, callbackName).

      2. If callbackValue is not undefined, then set the value of the entry in lifecycleCallbacks with key callbackName to the result of converting callbackValue to the Web IDL Function callback type. Rethrow any exceptions from the conversion.

    Then, perform the following substep, regardless of whether the above steps threw an exception or not:

    1. Unset this CustomElementRegistry's element definition is running flag.

    Finally, if the first set of substeps threw an exception, then rethrow that exception (thus terminating this algorithm). Otherwise, continue onward.

  15. Let definition be a new custom element definition with name name, local name localName, constructor constructor, observed attributes observedAttributes, lifecycle callbacks lifecycleCallbacks, form-associated formAssociated, disable internals disableInternals, and disable shadow disableShadow.

  16. Add definition to this CustomElementRegistry.

  17. Let document be this CustomElementRegistry's relevant global object's associated Document.

  18. Let upgrade candidates be all elements that are shadow-including descendants of document, whose namespace is the HTML namespace and whose local name is localName, in shadow-including tree order. Additionally, if extends is non-null, only include elements whose is value is equal to name.

  19. For each element element in upgrade candidates, enqueue a custom element upgrade reaction given element and definition.

  20. If this CustomElementRegistry's when-defined promise map contains an entry with key name:

    1. Let promise be the value of that entry.

    2. Resolve promise with constructor.

    3. Delete the entry with key name from this CustomElementRegistry's when-defined promise map.

When invoked, the get(name) method must run these steps:

  1. If this CustomElementRegistry contains an entry with name name, then return that entry's constructor.

  2. Otherwise, return undefined.

CustomElementRegistry/getName

Firefox116+Safari🔰 preview+Chrome117+
Opera?Edge117+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

The getName(constructor) method steps are:

  1. If this CustomElementRegistry contains an entry with constructor constructor, then return that entry's name.

  2. Return null.

When invoked, the whenDefined(name) method must run these steps:

  1. If name is not a valid custom element name, then return a promise rejected with a "SyntaxError" DOMException.

  2. If this CustomElementRegistry contains an entry with name name, then return a promise resolved with that entry's constructor.

  3. Let map be this CustomElementRegistry's when-defined promise map.

  4. If map does not contain an entry with key name, create an entry in map with key name and whose value is a new promise.

  5. Let promise be the value of the entry in map with key name.

  6. Return promise.

whenDefined()メソッドを使用して、すべての適切なカスタム要素定義されるまで、アクションを実行しないようにすることができる。この例において、使用する自律カスタム要素のすべてが定義されるまで、動的に読み込まれる記事のコンテンツを非表示するために、それを:defined疑似クラスと組み合わる。

articleContainer.hidden = true;

fetch(articleURL)
  .then(response => response.text())
  .then(text => {
    articleContainer.innerHTML = text;

    return Promise.all(
      [...articleContainer.querySelectorAll(":not(:defined)")]
        .map(el => customElements.whenDefined(el.localName))
    );
  })
  .then(() => {
    articleContainer.hidden = false;
  });

When invoked, the upgrade(root) method must run these steps:

  1. Let candidates be a list of all of root's shadow-including inclusive descendant elements, in shadow-including tree order.

  2. For each candidate of candidates, try to upgrade candidate.

upgrade()メソッドは、自由に要素をアップグレード可能にする。 通常、要素は接続されたときに自動的にアップグレードされるが、この方法は、要素を接続する準備ができる前にアップグレードする必要がある場合に使用できる。

const el = document.createElement("spider-man");

class SpiderMan extends HTMLElement {}
customElements.define("spider-man", SpiderMan);

console.assert(!(el instanceof SpiderMan)); // not yet upgraded

customElements.upgrade(el);
console.assert(el instanceof SpiderMan);    // upgraded!

4.13.5 Upgrades

To upgrade an element, given as input a custom element definition definition and an element element, run the following steps:

  1. If element's custom element state is not "undefined" or "uncustomized", then return.

    One scenario where this can occur due to reentrant invocation of this algorithm, as in the following example:

    <!DOCTYPE html>
    <x-foo id="a"></x-foo>
    <x-foo id="b"></x-foo>
    
    <script>
    // Defining enqueues upgrade reactions for both "a" and "b"
    customElements.define("x-foo", class extends HTMLElement {
      constructor() {
        super();
    
        const b = document.querySelector("#b");
        b.remove();
    
        // While this constructor is running for "a", "b" is still
        // undefined, and so inserting it into the document will enqueue a
        // second upgrade reaction for "b" in addition to the one enqueued
        // by defining x-foo.
        document.body.appendChild(b);
      }
    })
    </script>

    This step will thus bail out the algorithm early when upgrade an element is invoked with "b" a second time.

  2. Set element's custom element definition to definition.

  3. Set element's custom element state to "failed".

    It will be set to "custom" after the upgrade succeeds. For now, we set it to "failed" so that any reentrant invocations will hit the above early-exit step.

  4. For each attribute in element's attribute list, in order, enqueue a custom element callback reaction with element, callback name "attributeChangedCallback", and an argument list containing attribute's local name, null, attribute's value, and attribute's namespace.

  5. If element is connected, then enqueue a custom element callback reaction with element, callback name "connectedCallback", and an empty argument list.

  6. Add element to the end of definition's construction stack.

  7. Let C be definition's constructor.

  8. Run the following substeps while catching any exceptions:

    1. If definition's disable shadow is true and element's shadow root is non-null, then throw a "NotSupportedError" DOMException.

      This is needed as attachShadow() does not use look up a custom element definition while attachInternals() does.

    2. Set element's custom element state to "precustomized".

    3. Let constructResult be the result of constructing C, with no arguments.

      If C non-conformantly uses an API decorated with the [CEReactions] extended attribute, then the reactions enqueued at the beginning of this algorithm will execute during this step, before C finishes and control returns to this algorithm. Otherwise, they will execute after C and the rest of the upgrade process finishes.

    4. If SameValue(constructResult, element) is false, then throw a TypeError.

      This can occur if C constructs another instance of the same custom element before calling super(), or if C uses JavaScript's return-override feature to return an arbitrary HTMLElement object from the constructor.

    Then, perform the following substep, regardless of whether the above steps threw an exception or not:

    1. Remove the last entry from the end of definition's construction stack.

      Assuming C calls super() (as it will if it is conformant), and that the call succeeds, this will be the already constructed marker that replaced the element we pushed at the beginning of this algorithm. (The HTML element constructor carries out this replacement.)

      If C does not call super() (i.e. it is not conformant), or if any step in the HTML element constructor throws, then this entry will still be element.

    Finally, if the above steps threw an exception, then:

    1. Set element's custom element definition to null.

    2. Empty element's custom element reaction queue.

    3. Rethrow the exception (thus terminating this algorithm).

    If the above steps threw an exception, then element's custom element state will remain "failed" or "precustomized".

  9. If element is a form-associated custom element, then:

    1. Reset the form owner of element. If element is associated with a form element, then enqueue a custom element callback reaction with element, callback name "formAssociatedCallback", and « the associated form ».

    2. If element is disabled, then enqueue a custom element callback reaction with element, callback name "formDisabledCallback" and « true ».

  10. Set element's custom element state to "custom".

To try to upgrade an element, given as input an element element, run the following steps:

  1. Let definition be the result of looking up a custom element definition given element's node document, element's namespace, element's local name, and element's is value.

  2. If definition is not null, then enqueue a custom element upgrade reaction given element and definition.

4.13.6 カスタム要素応答

カスタム要素は、著者のコードを実行することで特定の出来事に応答する機能がある:

これらの反応をまとめてカスタム要素応答と呼ぶ。

カスタム要素反応が呼び出される方法は、デリケートな操作の途中で著者のコードが実行されるのを避けるために、特別な注意を払って行われる。実質的に、"ユーザースクリプトに戻る直前"まで遅延される。これは、ほとんどの目的では同期して実行されているように見えるが、複雑な複合操作(クローニング範囲操作など)の場合は、すべての関連するユーザーエージェントの処理ステップが完了するまで遅延され、その後、バッチとしてまとめて実行されることを意味する。

Additionally, the precise ordering of these reactions is managed via a somewhat-complicated stack-of-queues system, described below. このシステムの背後にある意図は、カスタム要素反応は常に、少なくとも1つのカスタム要素のローカルコンテキスト内で、トリガーアクションと同じ順序で常に呼び出されることを保証することである。(カスタム要素反応コードは独自の変更を実行できるため、複数の要素にまたがるグローバルな順序付けを保証することはできない。)


Each similar-origin window agent has a custom element reactions stack, which is initially empty. A similar-origin window agent's current element queue is the element queue at the top of its custom element reactions stack. Each item in the stack is an element queue, which is initially empty as well. Each item in an element queue is an element. (The elements are not necessarily custom yet, since this queue is used for upgrades as well.)

Each custom element reactions stack has an associated backup element queue, which an initially-empty element queue. Elements are pushed onto the backup element queue during operations that affect the DOM without going through an API decorated with [CEReactions], or through the parser's create an element for the token algorithm. An example of this is a user-initiated editing operation which modifies the descendants or attributes of an editable element. To prevent reentrancy when processing the backup element queue, each custom element reactions stack also has a processing the backup element queue flag, initially unset.

All elements have an associated custom element reaction queue, initially empty. Each item in the custom element reaction queue is of one of two types:

これは、次の模式図にすべて要約される:

A custom element reactions stack consists of a stack of element queues. Zooming in on a particular queue, we see that it contains a number of elements (in our example, <x-a>, then <x-b>, then <x-c>). Any particular element in the queue then has a custom element reaction queue. Zooming in on the custom element reaction queue, we see that it contains a variety of queued-up reactions (in our example, upgrade, then attribute changed, then another attribute changed, then connected).

To enqueue an element on the appropriate element queue, given an element element, run the following steps:

  1. Let reactionsStack be element's relevant agent's custom element reactions stack.

  2. If reactionsStack is empty, then:

    1. Add element to reactionsStack's backup element queue.

    2. If reactionsStack's processing the backup element queue flag is set, then return.

    3. Set reactionsStack's processing the backup element queue flag.

    4. Queue a microtask to perform the following steps:

      1. Invoke custom element reactions in reactionsStack's backup element queue.

      2. Unset reactionsStack's processing the backup element queue flag.

  3. Otherwise, add element to element's relevant agent's current element queue.

To enqueue a custom element callback reaction, given a custom element element, a callback name callbackName, and a list of arguments args, run the following steps:

  1. Let definition be element's custom element definition.

  2. Let callback be the value of the entry in definition's lifecycle callbacks with key callbackName.

  3. If callback is null, then return.

  4. If callbackName is "attributeChangedCallback", then:

    1. Let attributeName be the first element of args.

    2. If definition's observed attributes does not contain attributeName, then return.

  5. Add a new callback reaction to element's custom element reaction queue, with callback function callback and arguments args.

  6. Enqueue an element on the appropriate element queue given element.

To enqueue a custom element upgrade reaction, given an element element and custom element definition definition, run the following steps:

  1. Add a new upgrade reaction to element's custom element reaction queue, with custom element definition definition.

  2. Enqueue an element on the appropriate element queue given element.

To invoke custom element reactions in an element queue queue, run the following steps:

  1. While queue is not empty:

    1. Let element be the result of dequeuing from queue.

    2. Let reactions be element's custom element reaction queue.

    3. Repeat until reactions is empty:

      1. Remove the first element of reactions, and let reaction be that element. Switch on reaction's type:

        upgrade reaction
        Upgrade element using reaction's custom element definition.
        callback reaction
        Invoke reaction's callback function with reaction's arguments, and with element as the callback this value.

        If this throws an exception, catch it, and report the exception.


To ensure custom element reactions are triggered appropriately, we introduce the [CEReactions] IDL extended attribute. It indicates that the relevant algorithm is to be supplemented with additional steps in order to appropriately track and invoke custom element reactions.

The [CEReactions] extended attribute must take no arguments, and must not appear on anything other than an operation, attribute, setter, or deleter. Additionally, it must not appear on readonly attributes.

Operations, attributes, setters, or deleters annotated with the [CEReactions] extended attribute must run the following steps in place of the ones specified in their description:

  1. Push a new element queue onto this object's relevant agent's custom element reactions stack.

  2. Run the originally-specified steps for this construct, catching any exceptions. If the steps return a value, let value be the returned value. If they throw an exception, let exception be the thrown exception.

  3. Let queue be the result of popping from this object's relevant agent's custom element reactions stack.

  4. Invoke custom element reactions in queue.

  5. If an exception exception was thrown by the original steps, rethrow exception.

  6. If a value value was returned from the original steps, return value.

The intent behind this extended attribute is somewhat subtle. One way of accomplishing its goals would be to say that every operation, attribute, setter, and deleter on the platform must have these steps inserted, and to allow implementers to optimize away unnecessary cases (where no DOM mutation is possible that could cause custom element reactions to occur).

However, in practice this imprecision could lead to non-interoperable implementations of custom element reactions, as some implementations might forget to invoke these steps in some cases. Instead, we settled on the approach of explicitly annotating all relevant IDL constructs, as a way of ensuring interoperable behavior and helping implementations easily pinpoint all cases where these steps are necessary.

Any nonstandard APIs introduced by the user agent that could modify the DOM in such a way as to cause enqueuing a custom element callback reaction or enqueuing a custom element upgrade reaction, for example by modifying any attributes or child elements, must also be decorated with the [CEReactions] attribute.

As of the time of this writing, the following nonstandard or not-yet-standardized APIs are known to fall into this category:

4.13.7 要素内部

特定の機能は、カスタム要素の作成者が利用可能であることを意図されているが、カスタム要素の利用者は意図されていない。これらは、element.attachInternals()メソッドによって提供され、ElementInternalsのインスタンスを返す。ElementInternalsのプロパティおよびメソッドは、ユーザーエージェントがすべての要素に提供する内部機能の制御を可能にする。

element.attachInternals()

カスタム要素elementをターゲットとするElementInternalsオブジェクトを返す。elementカスタム要素でない場合、"internals"機能が要素定義の一部として無効にされた場合、または同じ要素で2回呼び出された場合、例外を投げる。

Each HTMLElement has an attached internals (null or an ElementInternals object), initially null.

HTMLElement/attachInternals

Support in all current engines.

Firefox93+Safari16.4+Chrome77+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

The attachInternals() method steps are:

  1. If this's is value is not null, then throw a "NotSupportedError" DOMException.

  2. Let definition be the result of looking up a custom element definition given this's node document, its namespace, its local name, and null as the is value.

  3. If definition is null, then throw an "NotSupportedError" DOMException.

  4. If definition's disable internals is true, then throw a "NotSupportedError" DOMException.

  5. If this's attached internals is non-null, then throw an "NotSupportedError" DOMException.

  6. If this's custom element state is not "precustomized" or "custom", then throw a "NotSupportedError" DOMException.

  7. Set this's attached internals to a new ElementInternals instance whose target element is this.

  8. Return this's attached internals.

4.13.7.1 The ElementInternals interface

ElementInternals

Support in all current engines.

Firefox93+Safari16.4+Chrome77+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

The IDL for the ElementInternals interface is as follows, with the various operations and attributes defined in the following sections:

[Exposed=Window]
interface ElementInternals {
  // Shadow root access
  readonly attribute ShadowRoot? shadowRoot;

  // Form-associated custom elements
  undefined setFormValue((File or USVString or FormData)? value,
                         optional (File or USVString or FormData)? state);

  readonly attribute HTMLFormElement? form;

  undefined setValidity(optional ValidityStateFlags flags = {},
                        optional DOMString message,
                        optional HTMLElement anchor);
  readonly attribute boolean willValidate;
  readonly attribute ValidityState validity;
  readonly attribute DOMString validationMessage;
  boolean checkValidity();
  boolean reportValidity();

  readonly attribute NodeList labels;

  // Custom state pseudo-class
  [SameObject] readonly attribute CustomStateSet states;
};

// Accessibility semantics
ElementInternals includes ARIAMixin;

dictionary ValidityStateFlags {
  boolean valueMissing = false;
  boolean typeMismatch = false;
  boolean patternMismatch = false;
  boolean tooLong = false;
  boolean tooShort = false;
  boolean rangeUnderflow = false;
  boolean rangeOverflow = false;
  boolean stepMismatch = false;
  boolean badInput = false;
  boolean customError = false;
};

ElementInternalsは、ターゲット要素を持ち、これはカスタム要素である。

4.13.7.2 シャドウルートアクセス
internals.shadowRoot

ターゲット要素シャドウホストである場合、 internalsターゲット要素ShadowRootを返し、そうでなければnullを返す。

ElementInternals/shadowRoot

Support in all current engines.

Firefox93+Safari16.4+Chrome88+
Opera?Edge88+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

The shadowRoot getter steps are:

  1. Let target be this's target element.

  2. If target is not a shadow host, then return null.

  3. Let shadow be target's shadow root.

  4. If shadow's available to element internals is false, then return null.

  5. Return shadow.

4.13.7.3 フォームに関連付けられたカスタム要素
internals.setFormValue(value)

internalsターゲット要素状態送信値の両方をvalueに設定する。

valueがnullである場合、要素はフォームの送信に参加しない。

internals.setFormValue(value, state)

internalsターゲット要素送信値valueに設定し、その状態stateに設定する。

valueがnullである場合、要素はフォームの送信に参加しない。

internals.form

internalsターゲット要素フォーム所有者を返す。

internals.setValidity(flags, message [, anchor ])

internalsターゲット要素flags引数で示された制約の影響を受けるものとしてマークし、要素の検証メッセージをmessageに設定する。anchorが指定される場合、ユーザーエージェントは、フォームの所有者がインタラクティブに検証される、またはreportValidity()が呼び出されたときに、internalsターゲット要素の制約に関する問題を示すために使用することがある。

internals.setValidity({})

internalsターゲット要素その制約を満たすものとしてマークする。

internals.willValidate

フォームの送信時にinternalsターゲット要素が検証される場合はtrueを返す。そうでなけれればfalseを返す。

internals.validity

internalsターゲット要素ValidityStateオブジェクトを返す。

internals.validationMessage

もしinternalsターゲット要素の妥当性をチェックしたならば、ユーザーに表示されるエラーメッセージを返すだろう。

valid = internals.checkValidity()

internalsターゲット要素に妥当性の問題がない場合はtrueを返す。 そうでなければfalseを返す。 後者の場合要素でinvalidイベントを発火する。

valid = internals.reportValidity()

internalsターゲット要素が一切妥当性の問題を持たない場合はtrueを返す。そうでなければfalseを返し、要素でinvalidイベントを発火させ、そして(イベントが中止されない場合)ユーザーに問題を報告する。

internals.labels

internalsターゲット要素が関連付けられているすべてのlabel 要素のNodeListを返す。

それぞれのフォームに関連付けられたカスタム要素送信値を持つ。フォーム送信時に1つ以上のエントリーを提供するために使用される。送信値の初期値はnullであり、送信値はnull、文字列、File、またはエントリーリストにすることができる。

それぞれのフォームに関連付けられたカスタム要素状態を持つ。これは、ユーザーエージェントが要素に対するユーザーの入力を復元できる情報である。状態の初期値はnullであり、状態はnull、文字列、File,、またはエントリーリストにすることができる。

カスタム要素の著者はsetFormValue()メソッドを使用して要素の送信値状態を設定し、これらをユーザーエージェントに伝達する。

ユーザーエージェントが、たとえばナビゲーション後やユーザーエージェントの再起動など、フォームに関連付けられたカスタム要素状態を復元するのがよいと考えている場合、その要素、コールバック名"formStateRestoreCallback"、復元する状態を含む引数リスト、および"restore"をもつカスタム要素のコールバック反応をエンキューしてもよい。

ユーザーエージェントがフォーム記入アシスト機能を持っている場合、その機能が呼び出されたときに、フォームに関連付けられたカスタム要素、コールバック名"formStateRestoreCallback"、状態値の履歴といくつかのヒューリスティックによって決定された状態値を含む引数リスト、および"autocomplete"とともにフォームに関連付けられたカスタム要素のコールバック反応をエンキューしてもよい。

一般に、状態はユーザーによって指定された情報であり、送信値はサーバーへの送信に適した、正規化またはサニタイズ後の値である。 次の例は、これを具体的に示している:

ユーザーに日付の指定を求めるフォームに関連付けられたカスタム要素があるとする。 ユーザーは"3/15/2019"を指定するが、コントロールはサーバーに"2019-03-15"を送信しようとする。"3/15/2019"は要素の状態であり、"2019-03-15"送信値である。

既存のチェックボックスinputタイプの動作をエミュレートするカスタム要素を開発するとする。 その送信値は、そのvalueコンテンツ属性の値、または文字列"on"になる。その状態は、"checked""unchecked""checked/indeterminate"、または"unchecked/indeterminate"のいずれかになる。

ElementInternals/setFormValue

Support in all current engines.

Firefox98+Safari16.4+Chrome77+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

The setFormValue(value, state) method steps are:

  1. Let element be this's target element.

  2. If element is not a form-associated custom element, then throw a "NotSupportedError" DOMException.

  3. Set target element's submission value to value if value is not a FormData object, or to a clone of value's entry list otherwise.

  4. If the state argument of the function is omitted, set element's state to its submission value.

  5. Otherwise, if state is a FormData object, set element's state to a clone of state's entry list.

  6. Otherwise, set element's state to state.


Each form-associated custom element has validity flags named valueMissing, typeMismatch, patternMismatch, tooLong, tooShort, rangeUnderflow, rangeOverflow, stepMismatch, and customError. They are false initially.

Each form-associated custom element has a validation message string. It is the empty string initially.

Each form-associated custom element has a validation anchor element. It is null initially.

ElementInternals/setValidity

Support in all current engines.

Firefox98+Safari16.4+Chrome77+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

The setValidity(flags, message, anchor) method steps are:

  1. Let element be this's target element.

  2. If element is not a form-associated custom element, then throw a "NotSupportedError" DOMException.

  3. If flags contains one or more true values and message is not given or is the empty string, then throw a TypeError.

  4. For each entry flagvalue of flags, set element's validity flag with the name flag to value.

  5. Set element's validation message to the empty string if message is not given or all of element's validity flags are false, or to message otherwise.

  6. If element's customError validity flag is true, then set element's custom validity error message to element's validation message. Otherwise, set element's custom validity error message to the empty string.

  7. Set element's validation anchor to null if anchor is not given. Otherwise, if anchor is not a shadow-including descendant of element, then throw a "NotFoundError" DOMException. Otherwise, set element's validation anchor to anchor.

ElementInternals/validationMessage

Support in all current engines.

Firefox98+Safari16.4+Chrome77+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

The validationMessage getter steps are:

  1. Let element be this's target element.

  2. If element is not a form-associated custom element, then throw a "NotSupportedError" DOMException.

  3. Return element's validation message.

The entry construction algorithm for a form-associated custom element, given an element element and an entry list entry list, consists of the following steps:

  1. If element's submission value is a list of entries, then append each item of element's submission value to entry list, and return.

    In this case, user agent does not refer to the name content attribute value. An implementation of form-associated custom element is responsible to decide names of entries. They can be the name content attribute value, they can be strings based on the name content attribute value, or they can be unrelated to the name content attribute.

  2. If the element does not have a name attribute specified, or its name attribute's value is the empty string, then return.

  3. If the element's submission value is not null, create an entry with the name attribute value and the submission value, and append it to entry list.

4.13.7.4 アクセシビリティセマンティックス
internals.role [ = value ]

internalsターゲット要素のデフォルトのARIAロールを設定または取得する。これは、ページの著者がrole属性を使用して上書きしない限り、使用される。

internals.aria* [ = value ]

internalsターゲット要素のさまざまなデフォルトのARIAステートまたはプロパティを設定または取得する。これは、ページの著者がaria-*属性を使用してそれらを上書きしない限り、使用される。

Each custom element has an internal content attribute map, which is a map, initially empty. See the Requirements related to ARIA and to platform accessibility APIs section for information on how this impacts platform accessibility APIs.

4.13.7.5 Custom state pseudo-class
internals.states.add(value)

Adds the string value to the element's states set to be exposed as a pseudo-class.

internals.states.has(value)

Returns true if value is in the element's states set, otherwise false.

internals.states.delete(value)

If the element's states set has value, then it will be removed and true will be returned. Otherwise, false will be returned.

internals.states.clear()

Removes all values from the element's states set.

for (const stateName of internals.states)
for (const stateName of internals.states.entries())
for (const stateName of internals.states.keys())
for (const stateName of internals.states.values())

Iterates over all values in the element's states set.

internals.states.forEach(callback)

Iterates over all values in the element's states set by calling callback once for each value.

internals.states.size

Returns the number of values in the element's states set.

Each custom element has a states set, which is a CustomStateSet, initially empty.

[Exposed=Window]
interface CustomStateSet {
  setlike<DOMString>;
};

The states getter steps are to return this's target element's states set.

The states set can expose boolean states represented by existence/non-existence of string values. If an author wants to expose a state which can have three values, it can be converted to three exclusive boolean states. For example, a state called readyState with "loading", "interactive", and "complete" values can be mapped to three exclusive boolean states, "loading", "interactive", and "complete":

// Change the readyState from anything to "complete".
this._readyState = "complete";
this._internals.states.delete("loading");
this._internals.states.delete("interactive");
this._internals.states.add("complete");