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 作成後の要素のアップグレード
      2. 4.13.2 カスタム要素のコンストラクタと反応の要件
      3. 4.13.3 コアコンセプト
      4. 4.13.4 CustomElementRegistryインターフェイス
      5. 4.13.5 カスタム要素応答
      6. 4.13.6 ElementInternalsインターフェイス

4.13 カスタム要素

Using_custom_elements

Support in all current engines.

Firefox63+Safari10.1+Chrome54+
Opera41+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android63+Safari iOS10.3+Chrome Android54+WebView Android54+Samsung Internet6.0+Opera Android41+
caniuse.com table

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 get observedAttributes() { return ["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 get formAssociated() { return true; }

  constructor() {
    super();
    this._internals = this.attachInternals();
    this._checked = false;
    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._checked; }
  set checked(flag) {
    this._checked = !!flag;
    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 カスタマイズされた組み込み要素の作成

カスタマイズされた組み込み要素は、自律カスタム要素とはわずかに異なる定義があり、そして大きく異なる使われ方をする、独自の種類のカスタム要素である。この要素は、新しいカスタム機能で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.4 自律カスタム要素の欠点

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

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

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

class TacoButton extends HTMLElement {
  static get observedAttributes() { return ["disabled"]; }

  constructor() {
    super();

    this.addEventListener("keydown", e => {
      if (e.keyCode === 32 || e.keyCode === 13) {
        this.dispatchEvent(new MouseEvent("click", {
          bubbles: true,
          cancelable: true
        }));
      }
    });

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

    this._observer = new MutationObserver(() => {
      this.setAttribute("aria-label", this.textContent);
    });
  }

  connectedCallback() {
    this.setAttribute("role", "button");
    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(v) {
    if (v) {
      this.setAttribute("disabled", "");
    } else {
      this.removeAttribute("disabled");
    }
  }

  attributeChangedCallback() {
    // only is called for the disabled attribute due to observedAttributes
    if (this.disabled) {
      this.removeAttribute("tabindex");
      this.setAttribute("aria-disabled", "true");
    } else {
      this.setAttribute("tabindex", "0");
      this.setAttribute("aria-disabled", "false");
    }
  }
}

このようにかなり複雑な要素定義があっても、その要素はコンシューマーのために使用する喜びとはならない。自身の意志のtabindexaria-*属性を継続的に"発芽"させる。これは、現在のところ、(通常はコンシューマーがデフォルトの動作を上書きできるように予約されているにもかかわらず)これらの属性を強制的に使用する必要があり、カスタム要素に対するデフォルトのアクセシビリティセマンティクスやフォーカス動作を指定する方法が存在しないためである。

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

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

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

Upgrades enable scenarios where it may be preferable for custom element definitions to be registered after relevant elements have been initially created, such as by the parser. They allow progressive enhancement of the content in the custom element. For example, in the following HTML document the element definition for img-viewer is loaded asynchronously:

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

The definition for the img-viewer element here is loaded using a script element marked with the async attribute, placed after the <img-viewer> tag in the markup. While the script is loading, the img-viewer element will be treated as an undefined element, similar to a span. Once the script loads, it will define the img-viewer element, and the existing img-viewer element on the page will be upgraded, applying the custom element's definition (which presumably includes applying an image filter identified by the string "Kelvin", enhancing the image's visual appearance).


Note that upgrades only apply to elements in the document tree. (Formally, elements that are connected.) An element that is not inserted into a document will stay un-upgraded. An example illustrates this point:

<!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.2 カスタム要素のコンストラクタと反応の要件

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

Several of these requirements are checked during element creation, either directly or indirectly, and failing to follow them will result in a custom element that cannot be instantiated by the parser or DOM APIs. This is true even if the work is done inside a constructor-initiated microtask, as a microtask checkpoint can occur immediately after construction.

When authoring custom element reactions, authors should avoid manipulating the node tree as this can lead to unexpected results.

An element's connectedCallback can be queued before the element is disconnected, but as the callback queue is still processed, it results in a connectedCallback for an element that is no longer connected:

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 コアコンセプト

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

Two distinct types of custom elements can be defined:

Global_attributes/is

Firefox63+SafariNoChrome67+
Opera55+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android63+Safari iOSNoChrome Android67+WebView Android67+Samsung Internet9.0+Opera Android48+
  1. An autonomous custom element, which is defined with no extends option. These types of custom elements have a local name equal to their defined name.

  2. A customized built-in element, which is defined with an extends option. These types of custom elements have a local name equal to the value passed in their extends option, and their defined name is used as the value of the is attribute, which therefore must be a valid custom element name.

After a custom element is created, changing the value of the is attribute does not change the element's behavior.

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

カテゴリー
フローコンテンツ
フレージングコンテンツ
パルパブルコンテンツ
フォーム関連カスタム要素の場合:Listedラベル付け可能送信可能リセット可能フォーム関連要素
この要素を使用できるコンテキスト
フレージングコンテンツが期待される場所。
コンテンツモデル
透過的
コンテンツ属性
Global attributes, except the is attribute
form, for form-associated custom elements — Associates the element with a form element
disabled, for form-associated custom elements — Whether the form control is disabled
readonly, for form-associated custom elements — Affects willValidate, plus any behavior added by the custom element author
name, for form-associated custom elements — Name of the element to use for form submission and in the form.elements API
名前空間を持たないその他の属性(文参照)。
アクセシビリティの考慮
フォーム関連カスタム要素の場合:著者向け 実装者向け
そうでなければ:著者向け実装者向け
DOMインターフェイス
Supplied by the element's author (inherits from HTMLElement)

An autonomous custom element does not have any special meaning: it represents its children. A customized built-in element inherits the semantics of the element that it extends.

Any namespace-less attribute that is relevant to the element's functioning, as determined by the element's author, may be specified on an autonomous custom element, so long as the attribute name is XML-compatible and contains no ASCII upper alphas. The exception is the is attribute, which must not be specified on an autonomous custom element (and which will have no effect if it is).

Customized built-in elements follow the normal requirements for attributes, based on the elements they extend. To add custom attribute-based behavior, use data-* attributes.


An autonomous custom element is called a form-associated custom element if the element is associated with a custom element definition whose form-associated field is set to true.

The name attribute represents the form-associated custom element's name. The disabled attribute is used to make the form-associated custom element non-interactive and to prevent its submission value from being submitted. The form attribute is used to explicitly associate the form-associated custom element with its form owner.

The readonly attribute of form-associated custom elements specifies that the element is barred from constraint validation. User agents don't provide any other behavior for the attribute, but custom element authors should, where possible, use its presence to make their control non-editable in some appropriate fashion, similar to the behavior for the readonly attribute on built-in form controls.

Constraint validation: If the readonly attribute is specified on a form-associated custom element, the element is barred from constraint validation.

The reset algorithm for form-associated custom elements is to enqueue a custom element callback reaction with the element, callback name "formResetCallback", and an empty argument list.


A valid custom element name is a sequence of characters name that meets all of the following requirements:

These requirements ensure a number of goals for valid custom element names:

Apart from these restrictions, a large variety of names is allowed, to give maximum flexibility for use cases like <math-α> or <emotion-😍>.

4.13.4 CustomElementRegistryインターフェイス

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

window . customElements . define(name, constructor)

CustomElementRegistry/define

Support in all current engines.

Firefox63+Safari10.1+Chrome66+
Opera53+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android63+Safari iOS10.3+Chrome Android66+WebView Android66+Samsung Internet9.0+Opera Android47+
Defines a new custom element, mapping the given name to the given constructor as an autonomous custom element.
window . customElements . define(name, constructor, { extends: baseLocalName })
Defines a new custom element, mapping the given name to the given constructor as a customized built-in element for the element type identified by the supplied baseLocalName. A "NotSupportedError" DOMException will be thrown upon trying to extend a custom element or an unknown element.
window . customElements . get(name)

CustomElementRegistry/get

Support in all current engines.

Firefox63+Safari10.1+Chrome66+
Opera53+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android63+Safari iOS10.3+Chrome Android66+WebView Android66+Samsung Internet9.0+Opera Android47+
Retrieves the custom element constructor defined for the given name. Returns undefined if there is no custom element definition with the given name.
window . customElements . whenDefined(name)

CustomElementRegistry/whenDefined

Support in all current engines.

Firefox63+Safari10.1+Chrome66+
Opera53+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android63+Safari iOS10.3+Chrome Android66+WebView Android66+Samsung Internet9.0+Opera Android47+
Returns a promise that will be fulfilled when a custom element becomes defined with the given name. (If such a custom element is already defined, the returned promise will be immediately fulfilled.) Returns a promise rejected with a "SyntaxError" DOMException if not given a valid custom element name.
window . customElements . upgrade(root)

CustomElementRegistry/upgrade

Firefox63+Safari?Chrome68+
Opera55+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android63+Safari iOS?Chrome Android68+WebView Android68+Samsung Internet10.0+Opera Android48+
Tries to upgrade all shadow-including inclusive descendant elements of root, even if they are not connected.

Element definition is a process of adding a custom element definition to the CustomElementRegistry. This is accomplished by the define() method.

The whenDefined() method can be used to avoid performing an action until all appropriate custom elements are defined. In this example, we combine it with the :defined pseudo-class to hide a dynamically-loaded article's contents until we're sure that all of the autonomous custom elements it uses are 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;
  });

The upgrade() method allows upgrading of elements at will. Normally elements are automatically upgraded when they become connected, but this method can be used if you need to upgrade before you're ready to connect the element.

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 カスタム要素応答

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

We call these reactions collectively custom element reactions.

The way in which custom element reactions are invoked is done with special care, to avoid running author code during the middle of delicate operations. Effectively, they are delayed until "just before returning to user script". This means that for most purposes they appear to execute synchronously, but in the case of complicated composite operations (like cloning, or range manipulation), they will instead be delayed until after all the relevant user agent processing steps have completed, and then run together as a batch.

It is guaranteed that custom element reactions always are invoked in the same order as their triggering actions, at least within the local context of a single custom element. (Because custom element reaction code can perform its own mutations, it is not possible to give a global ordering guarantee across multiple elements.)

4.13.6 ElementInternalsインターフェイス

element . attachInternals()

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

internals . setFormValue(value)

Sets both the state and submission value of internals's target element to value.

If value is null, the element won't participate in form submission.

internals . setFormValue(value, state)

Sets the submission value of internals's target element to value, and its state to state.

If value is null, the element won't participate in form submission.

internals . form

Returns the form owner of internals's target element.

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

Marks internals's target element as suffering from the constraints indicated by the flags argument, and sets the element's validation message to message. If anchor is specified, the user agent might use it to indicate problems with the constraints of internals's target element when the form owner is validated interactively or reportValidity() is called.

internals . setValidity({})

Marks internals's target element as satisfying its constraints.

internals . willValidate

Returns true if internals's target element will be validated when the form is submitted; false otherwise.

internals . validity

Returns the ValidityState object for internals's target element.

internals . validationMessage

Returns the error message that would be shown to the user if internals's target element was to be checked for validity.

valid = internals . checkValidity()

Returns true if internals's target element has no validity problems; false otherwise. Fires an invalid event at the element in the latter case.

valid = internals . reportValidity()

Returns true if internals's target element has no validity problems; otherwise, returns false, fires an invalid event at the element, and (if the event isn't canceled) reports the problem to the user.

internals . labels

Returns a NodeList of all the label elements that internals's target element is associated with.

Each ElementInternals has a target element, which is a custom element. ElementInternals provides various operations and attributes to allow control over internal features which the user agent provides to all elements.

Each HTMLElement has an attached internals boolean, initially false.

The attachInternals() method on an HTMLElement element, when invoked, must run the following steps:

  1. If element'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 element's node document, its namespace, its local name, and null as 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 element's attached internals is true, then throw an "NotSupportedError" DOMException.

  6. Set element's attached internals to true.

  7. Create a new ElementInternals instance targeting element, and return it.


Each form-associated custom element has submission value. It is used to provide one or more entries on form submission, and The initial value of submission value is null, and submission value can be null, a string, a File, or a list of entries.

Each form-associated custom element has state. It is information with which the user agent can restore a user's input for the element. The initial value of state is null, and state can be null, a string, a File, or a list of entries.

The setFormValue() method is used by the custom element author to set the element's submission value and state, thus communicating these to the user agent.

When the user agent believes it is a good idea to restore a form-associated custom element's state, for example after navigation or restarting the user agent, they may enqueue a custom element callback reaction with that element, callback name "formStateRestoreCallback", and an argument list containing the state to be restored, and "restore".

If the user agent has a form-filling assist feature, then when the feature is invoked, it may enqueue a custom element callback reaction with a form-associated custom element, callback name "formStateRestoreCallback", and an argument list containing the state value determined by history of state value and some heuristics, and "autocomplete".

In general, the state is information specified by a user, and the submission value is a value after canonicalization or sanitization, suitable for submission to the server. The following examples makes this concrete:

Suppose that we have a form-associated custom element which asks a user to specify a date. The user specifies "3/15/2019", but the control wishes to submit "2019-03-15" to the server. "3/15/2019" would be a state of the element, and "2019-03-15" would be a submission value.

Suppose you develop a custom element emulating a the behavior of the existing checkbox input type. Its submission value would be the value of its value content attribute, or the string "on". Its state would be one of "checked", "unchecked", "checked/indeterminate", or "unchecked/indeterminate".