1. 4.13 Custom elements
      1. 4.13.1 概述
        1. 4.13.1.1 创建自主的 Custom Element
        2. 4.13.1.2 Creating a form-associated custom element
        3. 4.13.1.3 Creating a custom element with default accessible roles, states, and properties
        4. 4.13.1.4 创建定制的内建元素
        5. 4.13.1.5 自主 custom element 的缺点
        6. 4.13.1.6 在创建之后升级元素
      2. 4.13.2 Requirements for custom element constructors and reactions
      3. 4.13.3 核心概念
      4. 4.13.4 CustomElementRegistry 接口
      5. 4.13.5 升级
      6. 4.13.6 Custom element reactions
      7. 4.13.7 Element internals
        1. 4.13.7.1 The ElementInternals interface
        2. 4.13.7.2 Shadow root access
        3. 4.13.7.3 Form-associated custom elements
        4. 4.13.7.4 Accessibility semantics

4.13 Custom elements

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+

4.13.1 概述

This section is non-normative.

Custom elements 为作者提供了一种构建他们自己的完整特性的 DOM 元素的方式。即使作者们总是可以在他们的文档中使用非标准元素, 在生效之后使用类似脚本的方式给它们增加应用相关的行为, 但是这些元素由于历史原因表现并不一致,也不是很好用。 通过 定义 custom element, 作者可以告知解析器如何适当地构造一个元素,以及这类元素如何响应变化。

Custom elements 是 "解释 Web 平台" 努力中的一部分, 在这一工作中,我们通过将低层的可扩展点(比如定义 Custom Element)暴露给作者, 来解释既有的平台特性(比如 HTML 的元素)。 即使现在 Custom Element 在功能和语义上都有很多能力的局限, 尚不能完整地解释现存 HTML 元素的行为,我们希望随着时间的推移逐渐缩小这个差异。

4.13.1.1 创建自主的 Custom Element

This section is non-normative.

为了说明如何创建 autonomous custom element, 我们定义 Custom Element 来封装一个显示国旗的小图标。 我们的目标是可以像这样使用它:

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

为了实现这一效果,我们首先为该 Custom Element 声明一个类,扩展 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)

最后,我们还可以直接使用 custom element 构造器。 这就是说上述代码等效于:

const flagIcon = new FlagIcon()
flagIcon.country = "jp"
document.body.appendChild(flagIcon)
4.13.1.2 Creating a form-associated custom element

This section is non-normative.

Adding a static formAssociated property, with a true value, makes an autonomous custom element a form-associated custom element. The ElementInternals interface helps you to implement functions and properties common to form control elements.

class MyCheckbox extends HTMLElement {
  static get formAssociated() { return true; }
  static get observedAttributes() { return ['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.getAttribute('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);

You can use the custom element my-checkbox like a built-in form-associated element. For example, putting it in form or label associates the my-checkbox element with them, and submitting the form will send data provided by my-checkbox implementation.

<form action="..." method="...">
  <label><my-checkbox name="agreed"></my-checkbox> I read the agreement.</label>
  <input type="submit">
</form>
4.13.1.3 Creating a custom element with default accessible roles, states, and properties

This section is non-normative.

By using the appropriate properties of ElementInternals, your custom element can have default accessibility semantics. The following code expands our form-associated checkbox from the previous section to properly set its default role and checkedness, as viewed by accessibility technology:

class MyCheckbox extends HTMLElement {
  static get formAssociated() { return true; }
  static get observedAttributes() { return ['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.getAttribute('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);

Note that, like for built-in elements, these are only defaults, and can be overridden by the page author using the role and aria-* attributes:

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

Custom element authors are encouraged to state what aspects of their accessibility semantics are strong native semantics, i.e., should not be overriden by users of the custom element. In our example, the author of the my-checkbox element would state that its role and aria-checked values are strong native semantics, thus discouraging code such as the above.

4.13.1.4 创建定制的内建元素

This section is non-normative.

定制的内建元素 是一种独特的 custom element,它的定义稍有不同,但使用方式与 自主的 custom elements 很不一样。 它们的存在是为了允许通过扩展新的定制功能来复用现存 HTML 元素的行为。 因为如果只使用 自主的 custom elements 很多 HTML 元素的现存行为可能需要重复实现, 但使用 定制的内建元素 允许给既有的元素 安装自定义的构造行为、生命周期钩子、以及原型链。 本质上就是在已经有的元素上 "混入" 这些定制的功能。

定制的内建元素 要求一种与 自主的 custom elements 不同的语法,因为用户代理和其他软件会提取元素的 局部名 来识别元素的语义和行为。 就是说建立在既有行为基础上的 定制的内建元素 的概念 严重依赖于被扩展的元素保持他们原有的 局部名。

在这个例子中,我们将会创建一个名为 plastic-button定制的内建元素。它像一个普通按钮一样,但是当点击时有漂亮的动画效果。 就像以前一样,我们从定义一个类开始,虽然这次我们扩展的是 HTMLButtonElement 而不是 HTMLElement

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

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

在定义我们的 custom element 时,我们必须同时声明 extends 选项:

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

通常被扩展元素的名字不能简单地通过看它扩展了什么元素接口来确定, 因为很多元素共用同样的接口(比如 qblockquote 共用 HTMLQuoteElement)。

为了使用我们的 定制内建元素, 我们使用 button 元素上的 is 属性:

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

尝试将 定制的内建元素 当做 自主 custom element 使用 不会 奏效; 也就是说 <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);          // 将会输出 "button"
console.log(plasticButton2.getAttribute("is")); // 将会输出 "plastic-button"

值得一提的是,所有 button 的特殊行为也适用于该 "plastic buttons": 获得焦点行为、参与 表单提交 的能力、 disabled 属性,等等。

定制的内建元素 设计为允许扩展既有 HTML 元素,尤其是那些用户代理提供了 有用行为或者 API 的元素。它们只能扩展在本标准中定义的现存 HTML 元素,不可继承遗留元素, 比如 bgsoundblinkisindexkeygenmulticolnextid, 或者 使用 HTMLUnknownElement 作为它们的 元素接口 来定义的 spacer

这一要求的一个原因是向后兼容性:如果定义了一个 定制内建元素,扩展了 一个目前未知的元素,比如 combobox,这将会阻止本标准在将来定义一个 combobox 元素,因为派生过 定制内建元素 的客户可能依赖于它们的基元素 没有那些有趣的用户代理提供的行为。

此外, applet 元素不可被扩展,因为它们正在被 Web 平台移除的过程中。

4.13.1.5 自主 custom element 的缺点

This section is non-normative.

正如下文规定,以及上文提到的,简单地定义和使用一个称为 taco-button 不意味着这个元素代表 represent 按钮。也就是说像 Web 浏览器、搜索引擎、 或可访问性技术这些工具将不会自动仅仅基于它定义的名字,就把它当做一个按钮。

为了向不同的用户表达期望的按钮语义,在使用 自主 custom element 的时候, 就有必要应用下面这些技术了:

考虑到这些点,一个全特性的 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-* 属性。 这是因为现在还没有办法设置 Custom Element 的默认的可访问性语义或者获得焦点行为。 只能强制使用这些属性来完成这一工作(即使它们通常是保留的,只供用户来重写默认行为)。

相比而言,一个简单的 定制内建元素, 如上一节所述,将会自动继承 button 的语义和行为,不需手动实现这些行为。 通常对于任何有着建立在既有 HTML 元素之上的明显语义和行为的元素, 定制内建元素 更容易开发、维护,以及使用。

4.13.1.6 在创建之后升级元素

This section is non-normative.

因为 元素定义 任何时候都可以发生, 可能会先 创建 一个非 custom element, 在适当的 定义 注册之后, 才变成一个 custom element。 我们称这一过程为 "升级" 元素:从一个普通元素编程一个 Custom Element。

升级 使得 custom element 定义 可以在 相关元素初(比如被解析器)始创建之后再行注册。这允许渐进增强 Custom Element 的内容。 例如在下列 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 元素的定义是被一个标记了 async 属性的 script 元素加载的。 在标记代码中该脚本放置在 <img-viewer> 标签之后。 当脚本正在载入时,img-viewer 元素将会被当做未定义元素, 类似 span。一旦脚本载入,它将会定义 img-viewer 元素, 并且页面中既有的 img-viewer 元素将会被升级, 应用 Custom Element 的定义(大概包括根据 "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.2 Requirements for custom element constructors and reactions

When authoring custom element constructors, authors are bound by the following conformance requirements:

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 核心概念

custom element自定义的 元素。 非形式化地,这意味着它的构造器和原型链由作者而不是用户代理定义。 作者提供的构造函数称为 custom element 构造器

可以定义两种不同的 custom elements

Global_attributes/is

Firefox63+SafariNoChrome67+
Opera54+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android63+Safari iOSNoChrome Android67+WebView Android67+Samsung Internet9.0+Opera Android48+
  1. 自主的 custom element 定义时不包含 extends 选项。这类 custom elements 的 局部名 等于它们 定义的名称

  2. 定制内建元素 定义时包含 extends 选项。这类 custom elements 的 局部名 等于它们 在 extends 选项中传入的值,它们的 定义的名称 被用于 is 属性的值,因此它必须是一个 合法的 custom element 名

custom element创建 后, 更改 is 属性的值不会更改该元素的行为,因为它只是 保存为该元素的 is 的值

自主的 custom elements 有下列的元素定义:

类别
流式内容
短语内容
可感知内容
表单相关的 custom element在列表中的, 可加标签的, 可提交的可重置的 表单相关元素
该元素可使用的上下文
应该出现 短语内容 的地方。
内容模型
透明的
内容属性
is 之外的 全局属性
表单相关元素form — Associates the element with a form element
表单相关元素disabled — Whether the form control is disabled
表单相关元素readonly — Affects willValidate, plus any behavior added by the custom element author
表单相关元素name — Name of the element to use for form submission and in the form.elements API
任何其他没有命名空间的属性。
可访问性相关
表单相关的 custom element使用方版本实现方版本
其他的:使用方版本实现方版本
DOM 接口
由元素作者提供(继承自 HTMLElement

自主的 custom element 没有任何特殊含义:它 表示 它的子节点。 定制内建元素 继承了它扩展的元素的语义。

任何没有命名空间的并与元素功能相关的属性,都可以按照元素作者的决定添加到 自主的 custom element 上, 只要该属性名是 XML 兼容的 其不含 ASCII 大写字母is 属性较为特殊, 它不能声明在 自主的 custom element 上(如果设置的话不会有任何效果)。

定制内建元素 遵循普通的属性名要求 (基于它们扩展的元素)。新增自定义的基于属性的行为,可使用 data-* 属性。


一个 自主的 custom element,如果与一个 form-associated 字段为 true 的 Custom Element 定义 相关联, 就称为 表单相关的 custom element

name 属性表示 表单相关的 custom element 的名字。 disabled 属性用来让 表单相关的 custom element 不可交互并阻止它的 提交数据 被提交。 form 属性用来明确地把 表单相关的 custom element 和它的 表单 owner 相关联。

readonly 属性指定该元素 禁止约束验证。用户代理不会给这个属性提供任何其他行为, 但 custom element 作者应该利用它是否存在来以某种方式让控件变得不可编辑,类似内置控件的 readonly 属性那样。

强校验:如果 表单相关的 custom element 的指定了 readonly 属性,该元素就 禁止了约束验证

表单相关的 custom element重置算法 是用这个元素 入队一个 Custom Element 反应,回调名为 "formResetCallback",参数列表为空。


合法的 custom element 名 是满足以下要求的一串字符 name

上述要求保证了 合法 custom element 名 的这些目标:

除这些限制之外,允许了大量的名字来给像 <math-α><emotion-😍> 这样的用例最大的自由。

Custom Element 定义 描述了一个 custom element 的组成:

name
一个 合法的 Custom Element 名称
局部名
一个 局部名
constructor
一个Web IDL Function 回调函数类型的值,包装了 custom element 构造器
被观察的属性列表
一个 sequence<DOMString>
生命周期回调列表
一个映射,其键是字符串 "connectedCallback", "disconnectedCallback", "adoptedCallback", "attributeChangedCallback", "formAssociatedCallback", "formDisabledCallback", "formResetCallback" 和 "formStateRestoreCallback"。 相应的值是 Web IDL Function 回调函数类型的值,或者 null。默认每一项的值都是null。
一个construction stack
一个初始为空的列表,由 元素升级 算法和 和 HTML 元素构造器 操作。列表中每一项都将是一个元素或者一个 已经构造的 标记
表单相关 布尔
如果为真,用户代理会把和这个 custom element 定义 关联的元素当作 表单相关的 custom elements 处理。
disable 内部 布尔
控制 attachInternals()
禁用 shadow 布尔
控制 attachShadow()

给定一个 documentnamespacelocalName,以及 is查找一个 custom element 的定义 应执行下列步骤( 返回一个 custom element 定义 或 null):

  1. 如果 namespace 不是 HTML 命名空间,返回 null。

  2. 如果 document浏览环境 是 null,返回 null。

  3. registrydocument相关全局对象CustomElementRegistry 对象。

  4. 如果在 registry 中有一个 custom element 定义name局部名 都等于 localName,返回该 custom element 定义

  5. 如果在 registry 中有一个 custom element 定义name 等于 is局部名 等于 localName,返回该 custom element 定义

  6. 返回 null。

4.13.4 CustomElementRegistry 接口

CustomElementRegistry

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+

每个 Window 对象都关联是一个唯一的 CustomElementRegistry 对象实例,当 Window 对象创建时分配。

Custom element registries 与 Window 对象而不是 Document 对象关联,因为每个 custom element 构造函数 继承自 HTMLElement 接口,而每个 Window 对象只有一个 HTMLElement 接口。

Window/customElements

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+

Window 接口的 customElements 属性必须返回该 Window 对象的 CustomElementRegistry 对象。

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

callback CustomElementConstructor = HTMLElement ();

dictionary ElementDefinitionOptions {
  DOMString extends;
};

每个 CustomElementRegistry 有一个 custom element 定义 的集合,初始为空。通常本标准中的算法在 registry 中通过 name局部名, 或 constructor 中的任意一个进行查找。

每个 CustomElementRegistry 还有一个 element 定义正在运行 标记, 用来防止 元素定义 被再次调用。 初始未设置。

每个 CustomElementRegistry 还有一个 when-defined Promise 映射, 将 合法的 custom element 名 映射为 Promise。 用来实现 whenDefined() 方法。

window . customElements . define(nameconstructor)

CustomElementRegistry/define

Support in all current engines.

Firefox63+Safari10.1+Chrome67+
Opera54+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android63+Safari iOS10.3+Chrome Android67+WebView Android67+Samsung Internet9.0+Opera Android48+
定义一个新的 custom element,作为 自主的 custom element,并将指定的名称映射到指定的构造函数。
window . customElements . define(nameconstructor, { extends: baseLocalName })
定义一个新的 custom element,作为 定制内建元素,其 元素类型baseLocalName 标识的。 同时将给定的 name 映射到给定的 constructor。当尝试扩展 custom element 或未知元素时, 将会抛出 "NotSupportedError" DOMException
window . customElements . get(name)

CustomElementRegistry/get

Support in all current engines.

Firefox63+Safari10.1+Chrome67+
Opera54+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android63+Safari iOS10.3+Chrome Android67+WebView Android67+Samsung Internet9.0+Opera Android48+
获取定义为 namecustom element 构造函数 。如果没有 custom element 定义 拥有给定的 name 则返回 undefined。
window . customElements . whenDefined(name)

CustomElementRegistry/whenDefined

Support in all current engines.

Firefox63+Safari10.1+Chrome67+
Opera54+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android63+Safari iOS10.3+Chrome Android67+WebView Android67+Samsung Internet9.0+Opera Android48+
返回一个 promise,定义给定 name 的 custom element 时完成,值为 custom element 的构造函数。 (如果这样的 custom element 已经定义,返回的 promise 将会立即完成。) 如果没有给定 合法的 Custom Element 名称 则返回一个已经以 "SyntaxError" DOMException 拒绝的 Promise。

元素定义 是向 CustomElementRegistry 新增一个 custom element 定义 的过程。 由 define() 方法完成。 define(nameconstructoroptions) 方法被调用时必须运行下列步骤:

  1. 如果 IsConstructor(constructor) 为 false,抛出一个 TypeError 并中止这些步骤。

  2. 如果 name 不是一个 合法的 custom element 名,则抛出一个 "SyntaxError" DOMException 并中止这些步骤。

  3. 如果该 CustomElementRegistry 包含 name 值为 name 的项目, 则抛出一个 "NotSupportedError" DOMException 并中止这些步骤。

  4. 如果该 CustomElementRegistry 包含 constructor 值为 constructor 的项目, 则抛出一个 "NotSupportedError" DOMException 并中止这些步骤。

  5. localNamename

  6. extendsoptionsextends 成员(没有该成员则为 null)。

  7. 如果 extends 不为 null,则:

    1. 如果 extends 是一个 合法的 custom element 名,则抛出 "NotSupportedError" DOMException

    2. 如果 extends元素接口HTML 命名空间HTMLUnknownElement (例如,如果 extends 表示的元素定义不在本标准中),则抛出 "NotSupportedError" DOMException

    3. 设置 localNameextends

  8. 如果该 CustomElementRegistry元素定义正在运行 标记被设置,则抛出一个 "NotSupportedError" DOMException 并中止这些步骤。

  9. 设置该 CustomElementRegistry元素定义正在运行 标记。

  10. formAssociated 为 false。

  11. disableInternals 为 false。

  12. disableShadow 为 false。

  13. observedAttributes 为空 sequence<DOMString>

  14. 运行下列子步骤并捕获所有异常:

    1. prototypeGet(constructor, "prototype")。重新抛出任何异常。

    2. 如果 Type(prototype) 不是 Object,则抛出 TypeError 异常。

    3. lifecycleCallbacks 为生命周期回调映射,并拥有键 "connectedCallback", "disconnectedCallback", "adoptedCallback", 和 "attributeChangedCallback", 每一项对应的值均为 null。

    4. lifecycleCallbacks 键中的每一项 callbackName,按照上一步列出的顺序:

      1. callbackValueGet(prototypecallbackName)。重新抛出任何异常。

      2. 如果 callbackValue 不是 undefined,则设置 lifecycleCallbacks 中键为 callbackName 的项目的值为 callbackValue 转换 为 Web IDL Function 回调类型的结果。重新抛出任何转换过程中的异常。

    5. 如果 lifecycleCallbacks 中键为 "attributeChangedCallback" 的项目的值不是 null,则:

      1. observedAttributesIterableGet(constructor, "observedAttributes")。 重新抛出任何异常。

      2. 如果 observedAttributesIterable 不是 undefined,则设置 observedAttributesobservedAttributesIterable 转换sequence<DOMString> 的结果。重新抛出任何转换过程中的异常。

    6. disabledFeatures 为空 sequence<DOMString>

    7. disabledFeaturesIterableGet(constructor, "disabledFeatures")。 重新抛出任何异常。

    8. 如果 disabledFeaturesIterable 不是 undefined,设置 disabledFeaturesdisabledFeaturesIterable 转换为 sequence<DOMString> 的结果。 重新抛出转换中的任何异常。

    9. 如果 disabledFeatures 包含 "internals", 设置 disableInternals 为 true。

    10. 如果 disabledFeatures 包含 "shadow", 设置 disableShadow 为 true。

    11. formAssociatedValueGet( constructor, "formAssociated")。重新抛出任何异常。

    12. 设置 formAssociatedformAssociatedValue 转换为 boolean 的结果。 重新抛出转换中的任何异常。

    13. 如果 formAssociated 为 true,对每个 "formAssociatedCallback", "formResetCallback", "formDisabledCallback" 和 "formStateRestoreCallback" callbackName

      1. callbackValueGet(prototype, callbackName)。重新抛出任何异常。

      2. 如果 callbackValue 不是 undefined,设置 lifecycleCallbacks 中键 callbackName 对应的值为 callbackValue 转换 为 Web IDL Function 回调类型的结果。重新抛出转换中的任何异常。

    然后执行下列的子步骤,不论上述步骤是否抛出异常:

    1. 重置 CustomElementRegistry元素定义正在运行 标记。

    最终,如果第一批子步骤抛出了异常,则重新抛出该异常并终止算法。否则继续。

  15. definition 为一个新的 custom element 定义, 其 namename, 其 局部名localName, 其 constructorconstructor, 其 observed attributesobservedAttributes, 其 lifecycle callbackslifecycleCallbacks, 其 form-associatedformAssociated, 其 disable internalsdisableInternals, 其 disable shadowdisableShadow

  16. 添加 definition 到该该 CustomElementRegistry

  17. document 为该 CustomElementRegistry相关全局对象关联的 Document

  18. upgrade candidatesdocument 的所有 shadow-including 后代 中, 命名空间为 HTML 命名空间 且 局部名 为 localName 的元素, 以 shadow-including 树序。 此外如果 extends 不是 null,则只包括 is 的值 等于 name 的元素。

  19. upgrade candidates 中的每一个元素 element将一个 Custom Element 升级响应加入队列,并指定 elementdefinition

  20. 如果该 CustomElementRegistrywhen-defined Promise 映射 包含键为 name 的项目:

    1. promise 为该项目的值。

    2. constructor 完成 promise

    3. 从该 CustomElementRegistrywhen-defined promise 映射 中删除键为 name 的项目。

get(name) 方法被调用时必须执行这些步骤:

  1. 如果该 CustomElementRegistry 包含 namename 条目,则返回该条目的 构造函数

  2. 否则返回 undefined。

whenDefined(name) 方法被调用时必须执行这些步骤:

  1. 如果 name 不是一个 合法的 custom element 名称,则返回一个新的 promise,以 "SyntaxError" DOMException 拒绝它并中止这些步骤。

  2. 如果该 CustomElementRegistry 包含一个 namename 的条目,则返回一个 Promise,其值为 那个条目的 构造函数

  3. map 为该 CustomElementRegistrywhen-defined promise 映射

  4. 如果 map 不包含键为 name 的条目,在 map 中创建一个条目, 其键为 name,值为一个新的 promise。

  5. promisemap 中键为 name 的条目的值。

  6. 返回 promise

whenDefined() 方法可以用来避免 在合适的 custom elements 被定义 之前执行操作。 这个例子中我们将它与 :defined 伪类结合, 来隐藏一个动态载入的文章内容,直到我们确定它使用的所有 自主 Custom Element 已经定义。

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

调用 upgrade(root) 方法时必须执行以下步骤:

  1. candidatesroot包括 shadow 的后代 所有元素的 列表, 按照 包括 shadow 的树排序

  2. 对每一个 candidates 中的 candidate尝试升级 candidate

upgrade() 方法允许按需升级元素。 正常情况下,元素会在 连接到 DOM 时自动升级, 但也可以在已经准备好连接到 DOM 时调用这个方法。

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 升级

给定输入 custom element 定义 definition 和一个元素 element升级元素 应运行以下步骤:

  1. 如果 element自定义的,中止这些步骤。

    这一情况在再次进入该算法时就会发生,比如下面的例子:

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

    这一步会使得第二次以 "b" 为参数调用 升级元素 算法时直接早退出。

  2. 如果 elementcustom element 状态failed",则中止这些步骤。

  3. element属性列表 中每一个 attribute, 按顺序 入队一个 Custom Element 反应 (元素为 element,回调名为 "attributeChangedCallback"), 参数列表为 attribute 的 局部名,null,attribute的值,以及 attribute 的命名空间。

  4. 如果 element 已连接入队一个 Custom Element 反应 (元素为 element,回调名为 "attributeChangedCallback"),参数列表为空。

  5. element 添加到 definition构造栈 尾部。

  6. Cdefinition构造器

  7. 运行以下子步骤的同时捕获任何异常:

    1. constructResult 为无参数 构造 C 的结果。

      如果 C 不合规范地 使用了以 [CEReactions] 扩展属性修饰的 API, 那么就在这一步中,在 C 结束前执行本算法开始时入队的回调,此后将控制返回给本算法。 否则,在 C 和其他升级过程结束后再执行它们。

    2. 如果 SameValue(constructResultelement) 为 false, 那么抛出一个 "InvalidStateError" DOMException

      如果 C 在调用 super() 之前构造了同一个 Custom Element 的另一个实例, 或者 C 使用了 JavaScript 的 return重写特性并在构造函数中返回一个任意的对象时, 就会发生这种情况。

    然后执行下列子步骤,无论上述步骤是否抛出了异常:

    1. definition构造栈 尾部移除最后一个入口。

      假设 C 调用了 super() (如果它 符合规范 就会这样做),并且该调用是成功的,此时 this 就是 已构造 标记,替换了本算法最开始推入的 element。 (就是 HTML 元素构造器 执行了这个替换。)

      如果 C 没有调用 super() (即它不 符合规范),或者在 HTML 元素构造 中的任何一步抛出了异常,那么这个入口仍然是 element

    最后,如果上述步骤抛出了异常:

    1. 设置 elementcustom element 状态 为 "failed"。

    2. 清空 elementcustom element 反应队列

    3. 重新抛出该异常,终止本算法。

  8. 设置 elementcustom element 状态 为 "custom"。

  9. 设置 elementcustom element 定义definition

给定输入元素 element尝试升级元素, 应执行下列步骤:

  1. definition 为给定 element节点文档element的命名空间, element的 局部名,以及 elementis查找 custom element 定义 的结果。

  2. 如果 definition 不是 null,那么 入队一个 Custom Element 升级反应, 其元素为 element,定义为 definition

4.13.6 Custom element reactions

A custom element possesses the ability to respond to certain occurrences by running author code:

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.

Additionally, the precise ordering of these reactions is managed via a somewhat-complicated stack-of-queues system, described below. The intention behind this system is to guarantee 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.)


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:

This is all summarized in the following schematic diagram:

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. For each custom element element in queue:

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

    2. 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 internals

Certain capabilities are meant to be available to a custom element author, but not to a custom element consumer. These are provided by the element.attachInternals() method, which returns an instance of ElementInternals. The properties and methods of ElementInternals allow control over internal features which the user agent provides to all elements.

element . attachInternals()

Returns an ElementInternals object targeting the custom element element. Throws an exception if element is not a custom element, if the "internals" feature was disabled as part of the element definition, or if it is called twice on the same element.

Each HTMLElement has an attached internals boolean, initially false.

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 true, 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 true.

  8. Return a new ElementInternals instance whose target element is this.

4.13.7.1 The ElementInternals interface

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;
};

// 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;
};

Each ElementInternals has a target element, which is a custom element.

4.13.7.2 Shadow root access
internals . shadowRoot

Returns the ShadowRoot for internals's target element, if the target element is a shadow host, or null otherwise.

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 Form-associated custom elements
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 form-associated custom element has submission value. It is used to provide one or more entries on form submission. 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".

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 the entry list associated with value 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 clone of the entry list associated with state.

  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.

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.

The validationMessage getter steps are to return the validation message of this's target element.

The entry construction algorithm for a form-associated custom element, given an element element and a 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, append an entry to entry list with the name attribute value and the submission value.

4.13.7.4 Accessibility semantics
internals . role [ = value ]

Sets or retrieves the default ARIA role for internals's target element, which will be used unless the page author overrides it using the role attribute.

internals . aria* [ = value ]

Sets or retrieves various default ARIA states or property values for internals's target element, which will be used unless the page author overrides them using the aria-* attributes.

Each custom element has a native accessibility semantics 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.

ElementInternals includes the ARIAMixin mixin. The accessors provided by this mixin are used to manipulate the target element's native accessibility semantics map, as follows:

The ARIAMixin getter steps for ElementInternals, given internals, idlAttribute, and contentAttribute, are:

  1. Let map be internals's target element's native accessibility semantics map.

  2. If map[contentAttribute] exists, then return it.

  3. Return null.

The ARIAMixin setter steps for ElementInternals, given internals, idlAttribute, contentAttribute, and value, are:

  1. Let map be internals's target element's native accessibility semantics map.

  2. If value is null, then remove map[contentAttribute].

  3. Otherwise, set map[contentAttribute] to value.