<span class="latin" style="width:19px;height:19px;">H</span><span class="latin" style="width:19px;height:19px;">T</span><span class="latin" style="width:19px;height:19px;">M</span><span class="latin" style="width:19px;height:19px;">L</span><span class="latin" style="display:block;width:19px;height:19px;"> </span><span class="latin" style="width:19px;height:19px;">S</span><span class="latin" style="width:19px;height:19px;">t</span><span class="latin" style="width:19px;height:19px;">a</span><span class="latin" style="width:19px;height:19px;">n</span><span class="latin" style="width:19px;height:19px;">d</span><span class="latin" style="width:19px;height:19px;">a</span><span class="latin" style="width:19px;height:19px;">r</span><span class="latin" style="width:19px;height:19px;">d</span>  
(一)4.13 Custom elements
(一)4.13.1 Introduction
(一)4.13.1.1 Creating an autonomous custom element
(二)4.13.1.2 Creating a form-associated custom element
(三)4.13.1.3 Creating a custom element with default accessible roles, states, and properties
(四)4.13.1.4 Creating a customized built-in element
(五)4.13.1.5 Drawbacks of autonomous custom elements
(六)4.13.1.6 Upgrading elements after their creation
(七)4.13.1.7 Scoped custom element registries
(八)4.13.1.8 Exposing custom element states
(二)4.13.2 Requirements for custom element constructors and  reactions
(一)4.13.2.1 Preserving custom element state when moved
(三)4.13.3 Core concepts
(四)4.13.4 The CustomElementRegistry interface
(五)4.13.5 Upgrades
(六)4.13.6 Custom element reactions
(七)4.13.7 Element internals
(一)4.13.7.1 The ElementInternals interface
(二)4.13.7.2 Shadow root access
(三)4.13.7.3 Form-associated custom elements
(四)4.13.7.4 Accessibility semantics
(五)4.13.7.5 Custom state pseudo-class

4.13 Custom elements


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 Introduction


This section is non-normative.

Custom elements provide a way for authors to build their  own fully-featured DOM elements. Although authors could always use non-standard elements in their  documents, with application-specific behavior added after the fact by scripting or similar, such  elements have historically been non-conforming and not very functional. By defining a custom element, authors can inform the parser how to  properly construct an element and how elements of that class should react to changes.

Custom elements are part of a larger effort to "rationalise the platform", by explaining  existing platform features (like the elements of HTML) in terms of lower-level author-exposed  extensibility points (like custom element definition). Although today there are many limitations  on the capabilities of custom elementsboth functionally and semanticallythat prevent them from  fully explaining the behaviors of HTML's existing elements, we hope to shrink this gap over  time.
4.13.1.1 Creating an autonomous custom element

This section is non-normative.

For the purposes of illustrating how to create an autonomous custom element, let's  define a custom element that encapsulates rendering a small icon for a country flag. Our goal is  to be able to use it like so:
 
<flag-icon country="nl"></flag-icon>

To do this, we first declare a class for the custom element, extending  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.
  }
}

We then need to use this class to define the element:
 
customElements.define("flag-icon", FlagIcon);

At this point, our above code will work! The parser, whenever it sees the flag-icon tag, will construct a new instance of our FlagIcon class, and tell our code about its new country  attribute, which we then use to set the element's internal state and update its rendering (when  appropriate).

You can also create flag-icon elements using the DOM API:
 
const flagIcon = document.createElement("flag-icon")
flagIcon.country = "jp"
document.body.appendChild(flagIcon)

Finally, we can also use the custom element constructor itself. That is, the above  code is equivalent to:
 
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 elementaform-associated custom element. The  ElementInternals interface helps you to implement functions and properties common  to form control elements.
 
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);

You can use the custom element my-checkbox like a built-in  form-associated element. For example, putting it in formorlabel  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 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);

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 overridden 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 Creating a customized built-in element

This section is non-normative.

Customized built-in elements are a distinct  kind of custom element, which are defined slightly differently and used very  differently compared to autonomous custom  elements. They exist to allow reuse of behaviors from the existing elements of HTML, by  extending those elements with new custom functionality. This is important since many of the  existing behaviors of HTML elements can unfortunately not be duplicated by using purely autonomous custom elements. Instead, customized built-in elements allow the installation of  custom construction behavior, lifecycle hooks, and prototype chain onto existing elements,  essentially "mixing in" these capabilities on top of the already-existing element.

Customized built-in elements require a  distinct syntax from autonomous custom elements  because user agents and other software key off an element's local name in order to identify the  element's semantics and behavior. That is, the concept of customized built-in elements building on top of existing behavior depends  crucially on the extended elements retaining their original local name.

In this example, we'll be creating a customized built-in element named plastic-button, which behaves like a normal button but gets fancy animation  effects added whenever you click on it. We start by defining a class, just like before, although  this time we extend HTMLButtonElement instead of HTMLElement:
 
class PlasticButton extends HTMLButtonElement {
  constructor() {
    super();

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

When defining our custom element, we have to also specify the extends option:
 
customElements.define("plastic-button", PlasticButton, { extends: "button" });

In general, the name of the element being extended cannot be determined simply by looking at  what element interface it extends, as many elements share the same interface (such as  qand blockquote both sharing HTMLQuoteElement).

To construct our customized built-in element from parsed HTML source text, we use  the isattribute on a button element:
 
<button is="plastic-button">Click Me!</button>

Trying to use a customized built-in element as an autonomous custom  element will not work; that is, <plastic-button>Click  me?</plastic-button> will simply create an HTMLElement with no special  behavior.

If you need to create a customized built-in element programmatically, you can use the following  form of createElement():
 
const plasticButton = document.createElement("button", {is: "plastic-button" });
plasticButton.textContent = "Click me!";

And as before, the constructor will also work:
 
const plasticButton2 = new PlasticButton();
console.log(plasticButton2.localName);  // will output "button"
console.assert(plasticButton2 instanceof PlasticButton);
console.assert(plasticButton2 instanceof HTMLButtonElement);

Note that when creating a customized built-in element programmatically, the isattribute will not be present in the DOM, since it was not explicitly  set. However, it will be added to the output when  serializing:
 
console.assert(!plasticButton.hasAttribute("is"));
console.log(plasticButton.outerHTML); // will output '<button is="plastic-button"></button>'

Regardless of how it is created, all of the ways in which button is special  apply to such "plastic buttons" as well: their focus behavior, ability to participate in form submission, the disabled attribute, and so on.

Customized built-in elements are designed to allow extension of existing HTML  elements that have useful user-agent supplied behavior or APIs. As such, they can only extend  existing HTML elements defined in this specification, and cannot extend legacy elements such as  bgsound, blink, isindex, keygen,  multicol, nextid, or spacer that have been defined to use  HTMLUnknownElement as their element interface.

One reason for this requirement is future-compatibility: if a customized built-in  element was defined that extended a currently-unknown element, for example combobox, this would prevent this specification from defining a combobox element in the future, as consumers of the derived customized  built-in element would have come to depend on their base element having no interesting  user-agent-supplied behavior.
4.13.1.5 Drawbacks of autonomous custom elements

This section is non-normative.

As specified below, and alluded to above, simply defining and using an element called  taco-button does not mean that such elements represent buttons. That is, tools such as web browsers, search engines,  or accessibility technology will not automatically treat the resulting element as a button just  based on its defined name.

To convey the desired button semantics to a variety of users, while still using an  autonomous custom element, a number of techniques would need to be employed:

The addition of the tabindex attribute would make the  taco-button focusable. Note that if the  taco-button were to become logically disabled, the tabindex attribute would need to be removed.
The addition of an ARIA role and various ARIA states and properties helps convey semantics  to accessibility technology. For example, setting the role to "button" will convey the semantics that this is a button,  enabling users to successfully interact with the control using usual button-like interactions in  their accessibility technology. Setting the aria-label  property is necessary to give the button an accessible  name, instead of having accessibility technology traverse its child text nodes and  announce them. And setting the aria-disabled state to  "true" when the button is logically disabled conveys to accessibility  technology the button's disabled state.
The addition of event handlers to handle commonly-expected button behaviors helps convey  the semantics of the button to web browser users. In this case, the most relevant event handler  would be one that proxies appropriate keydown events to  become click events, so that you can activate the button both  with keyboard and by clicking.
In addition to any default visual styling provided for taco-button  elements, the visual styling will also need to be updated to reflect changes in logical state,  such as becoming disabled; that is, whatever style sheet has rules for taco-button will also need to have rules for taco-button[disabled].

With these points in mind, a full-featured taco-button that took on the  responsibility of conveying button semantics (including the ability to be disabled) might look  something like this:
 
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";
    }
  }
}

Even with this rather-complicated element definition, the element is not a pleasure to use for  consumers: it will be continually "sprouting" tabindex  attributes of its own volition, and its choice of tabindex="0" focusability  behavior may not match the button behavior on the current platform. This is because  as of now there is no way to specify default focus behavior for custom elements, forcing the use  of the tabindex attribute to do so (even though it is usually  reserved for allowing the consumer to override default behavior).

In contrast, a simple customized built-in element, as shown in the previous  section, would automatically inherit the semantics and behavior of the button  element, with no need to implement these behaviors manually. In general, for any elements with  nontrivial behavior and semantics that build on top of existing elements of HTML, customized built-in elements will be easier to  develop, maintain, and consume.
4.13.1.6 Upgrading elements after their creation

This section is non-normative.

Because element definition can occur at any time, a non-custom element could be  created, and then later become a custom  element after an appropriate definition is  registered. We call this process "upgrading" the element, from a normal element into a custom  element.

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.1.7 Scoped custom element registries

To allow multiple libraries to co-exist without explicit coordination,  CustomElementRegistry can be used in a scoped fashion as well.
 
const scoped = new CustomElementRegistry();
scoped.define("example-element", ExampleElement);

const element = document.createElement("example-element", { customElementRegistry: scoped });

A node with an associated scoped CustomElementRegistry will use that registry for  all its operations, such as when invoking setHTMLUnsafe().
4.13.1.8 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 Requirements for custom element constructors and  reactions


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

A parameter-less call to super() must be the first statement in the  constructor body, to establish the correct prototype chain and this value before any  further code is run.
Areturn statement must not appear anywhere inside the constructor  body, unless it is a simple early-return (returnorreturn  this).
The constructor must not use the document.write()ordocument.open() methods.
The element's attributes and children must not be inspected, as in the non-upgrade case none will be present, and relying on  upgrades makes the element less usable.
The element must not gain any attributes or children, as this violates the expectations of  consumers who use the createElementorcreateElementNS methods.
In general, work should be deferred to connectedCallback as much as  possibleespecially work involving fetching resources or rendering. However, note that connectedCallback can be called more than once, so any initialization work that  is truly one-time will need a guard to prevent it from running twice.
In general, the constructor should be used to set up initial state and default values, and  to set up event listeners and possibly a shadow root.

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.2.1 Preserving custom element state when moved

This section is non-normative.

When manipulating the DOM tree, an element can be moved in the tree while connected. This applies to custom  elements as well. By default, the "disconnectedCallback" and "connectedCallback" would be called on the element, one after the other. This is  done to maintain compatibility with existing custom elements that predate the moveBefore() method. This means that by default, custom  elements reset their state as if they were removed and re-inserted. In the example above, the impact would be that the  observer would be disconnected and re-connected, and the tab index would be reset.

To opt in to a state-preserving behavior while moving, the author can implement a "connectedMoveCallback". The existence of this callback, even if empty, would  supersede the default behavior of calling "disconnectedCallback" and "connectedCallback". "connectedMoveCallback" can also be an  appropriate place to execute logic that depends on the element's ancestors. For example:
 
class TacoButton extends HTMLElement {
  static observedAttributes = ["disabled"];

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

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

  _notifyMain() {
    if (this.parentElement.tagName === "MAIN") {
      // Execute logic that depends on ancestors.
    }
  }

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

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

    this._notifyMain();
  }

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

  // Implementing this function would avoid resetting the tab index or re-registering the
  // mutation observer when this element is moved inside the DOM without being disconnected.
  connectedMoveCallback() {
    // The parent can change during a state-preserving move.
    this._notifyMain();
  }
}

4.13.3 Core concepts


Acustom element is an element that is custom. Informally, this means that its constructor and  prototype are defined by the author, instead of by the user agent. This author-supplied  constructor function is called the custom element constructor.

Two distinct types of custom elements can be defined:


Global_attributes/is
Firefox63+SafariNoChrome67+

Opera?Edge79+

Edge (Legacy)?Internet ExplorerNo

Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
Anautonomous custom element, which is defined with no extends option. These types of custom  elements have a local name equal to their defined name.
Acustomized 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  isattribute, which  therefore must be a valid custom element name.

After a custom elementiscreated,  changing the value of the isattribute does not  change the element's behavior, as it is saved on the element as its isvalue.

Autonomous custom elements have the following  element definition:

Categories:
Flow content.
Phrasing content.
Palpable content.
For form-associated custom elements: Listed, labelable, submittable, and resettable form-associated element.
Contexts in which this element can be used:
Where phrasing content is expected.
Content model:
Transparent.
Content attributes:
Global attributes, except the isattribute
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 
Any other attribute that has no namespace (see prose).
Accessibility considerations:
For form-associated custom elements: for authors; for implementers.
Otherwise: for authors; for implementers.
DOM interface:
Supplied by the element's author (inherits from HTMLElement)

Anautonomous 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 a valid attribute local name and contains no ASCII upper alphas. The exception is the isattribute, 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.



Anautonomous 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 « ».
 



A string name is a valid custom element name if all of the  following are true:


name is a valid element local name;

This ensures the custom element can be created with createElement().


name's 0th code point is an ASCII lower alpha;

This ensures the HTML parser will treat the name as a tag name instead of as  text.


name does not contain any ASCII upper  alphas;

This ensures the user agent can always treat HTML elements  ASCII-case-insensitively.


name contains a U+002D (-); and

This is used for namespacing and to ensure forward compatibility (since no  elements will be added to HTML, SVG, or MathML with hyphen-containing local names going  forward).


name is not one of the following:

"annotation-xml"
"color-profile"
"font-face"
"font-face-src"
"font-face-uri"
"font-face-format"
"font-face-name"
"missing-glyph"

The list of names above is the summary of all hyphen-containing element names  from the applicable specifications, namely  SVG 2 and MathML. [SVG] [MATHML]
 


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


Acustom element definition describes a custom element and  consists of:

Aname
Avalid custom element name
Alocal name
A local name
Aconstructor
A Web IDL CustomElementConstructor callback function type value wrapping  the custom element constructor
A list of observed  attributes
Asequence<DOMString>
A collection of lifecycle callbacks
A map, whose keys are the strings "connectedCallback", "disconnectedCallback", "adoptedCallback", "connectedMoveCallback", "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.
Aconstruction  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.
Aform-associated  boolean
If this is true, user agent treats elements associated to this  custom element definitionasform-associated custom elements.
Adisable  internals boolean
Controls attachInternals().
Adisable shadow boolean
Controls attachShadow().  


Tolook up a custom element definition, given null or a  CustomElementRegistry object registry, string-or-null  namespace, string localName, and string-or-null is, perform the  following steps. They will return either a custom element definition or null:

Ifregistry is null, then return null.
Ifnamespace is not the HTML namespace, then return null.
Ifregistry's custom element definition set contains an item with name and local name both equal to  localName, then return that item.
Ifregistry's custom element definition set contains an item with name equal to isand local name equal to  localName, then return that item.
Return null.
 

4.13.4 The CustomElementRegistry interface


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 similar-origin window agent has an associated active custom element constructor map, which is a map of constructors to CustomElementRegistry objects.


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 Window customElements getter steps are:


Assert: this's associated  Document's custom element  registry is a CustomElementRegistry object.

AWindow's associated  Document is always created with a new  CustomElementRegistry object.

Return this's associated  Document's custom element  registry.
 
[Exposed=Window]
interface CustomElementRegistry {
  constructor();

  [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);
  [CEReactions] undefined initialize(Node root);
};

callback CustomElementConstructor = HTMLElement ();

dictionary ElementDefinitionOptions {
  DOMString extends;
};

Every CustomElementRegistry has an is  scoped, a boolean, initially false.

Every CustomElementRegistry has a scoped  document set, a setofDocument objects, initially « ».

Every CustomElementRegistry has a custom element definition set, a  setofcustom element definitions,  initially « ». Lookup of items in this set uses their name, local name, or constructor.

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

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


Tolook up a custom element registry, given a Node object  node:

Ifnode is an Element object, then return node's custom element registry.
Ifnode is a ShadowRoot object, then return node's  custom element registry.
Ifnode is a Document object, then return node's custom element registry.
Return null.
 

registry = window.customElements
Returns the global's associated Document's CustomElementRegistry  object.
registry = new CustomElementRegistry()
Constructs a new CustomElementRegistry object, for scoped usage.
registry.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?
Defines a new custom element, mapping the given name to the given constructor  as an autonomous custom element.
registry.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, or when registry is not a global  CustomElementRegistry object.
registry.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?
Retrieves the custom element constructor defined for the given name. Returns undefined if there is no  custom element definition with the given name.
registry.getName(constructor)
Retrieves the given name for a custom element defined for the given constructor. Returns null if there is no  custom element definition with the given constructor.
registry.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?
Returns a promise that will be fulfilled with the custom element's constructor  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.
registry.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?
Tries to upgrade all shadow-including  inclusive descendant elements of root, even if they are not  connected.
registry.initialize(root)
Each inclusive descendantofroot with a null registry will have it  updated to this CustomElementRegistry object. A  "NotSupportedError" DOMException will be thrown if this  CustomElementRegistry object is not for scoped usage and either root is a  Document node or root's node document's custom element registry is not this  CustomElementRegistry object.


The new CustomElementRegistry() constructor  steps are to set this's is scoped to true.
 

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


The define(name, constructor,  options) method steps are:

IfIsConstructor(constructor) is false, then throw a  TypeError.
Ifname is not a valid custom element name, then throw a  "SyntaxError" DOMException.
Ifthis's custom element definition set contains an item with name name, then throw a  "NotSupportedError" DOMException.
Ifthis's custom element definition set contains an item with constructor constructor,  then throw a "NotSupportedError" DOMException.
Let localNamebename.
Let extendsbeoptions["extends"] if it exists; otherwise null.

Ifextends is not null:

Ifthis's is scoped is true, then throw a  "NotSupportedError" DOMException.
Ifextends is a valid custom element name, then throw a  "NotSupportedError" DOMException.
If the element interface for extends and the HTML  namespaceisHTMLUnknownElement (e.g., if extends does not  indicate an element definition in this specification), then throw a  "NotSupportedError" DOMException.
Set localNametoextends.

Ifthis's element definition is running is true, then throw  a "NotSupportedError" DOMException.
Set this's element definition is running to true.
Let formAssociated be false.
Let disableInternals be false.
Let disableShadow be false.
Let observedAttributes be an empty sequence<DOMString>.

Run the following steps while catching any exceptions:

Let prototype be ? Get(constructor,  "prototype").
Ifprototype is not an Object, then throw a  TypeError exception.
Let lifecycleCallbacks be the ordered map «[ "connectedCallback"  null, "disconnectedCallback"   null, "adoptedCallback"  null, "connectedMoveCallback"  null, "attributeChangedCallback"  null ]».


For each callbackNameofthe keysoflifecycleCallbacks:

Let callbackValue be ? Get(prototype,  callbackName).

IfcallbackValue is not undefined, then set  lifecycleCallbacks[callbackName] to the result of converting callbackValue to the Web IDL Function callback type.


IflifecycleCallbacks["attributeChangedCallback"] is not  null:

Let observedAttributesIterable be ? Get(constructor, "observedAttributes").
IfobservedAttributesIterable is not undefined, then set  observedAttributes to the result of converting observedAttributesIterable to a  sequence<DOMString>. Rethrow any exceptions from the  conversion.

Let disabledFeatures be an empty sequence<DOMString>.
Let disabledFeaturesIterable be ? Get(constructor, "disabledFeatures").
IfdisabledFeaturesIterable is not undefined, then set  disabledFeatures to the result of converting disabledFeaturesIterable to a sequence<DOMString>. Rethrow any exceptions from the conversion.
IfdisabledFeatures contains "internals", then set disableInternals to true.
IfdisabledFeatures contains "shadow", then set disableShadow to true.
Let formAssociatedValue be ? Get(  constructor, "formAssociated").
Set formAssociated to the result of converting formAssociatedValue to a boolean.

IfformAssociated is true, then for each callbackName of « "formAssociatedCallback", "formResetCallback", "formDisabledCallback", "formStateRestoreCallback"  »:

Let callbackValue be ? Get(prototype,  callbackName).
IfcallbackValue is not undefined, then set  lifecycleCallbacks[callbackName] to the result of converting callbackValue to the Web IDL Function callback type.
 

Then, regardless of whether the above steps threw an exception or not: set  this's element definition is running to false.

Finally, if the steps threw an exception, rethrow that exception.

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.
Append definitiontothis's  custom element definition set.
Ifthis's is scoped is true, then for each documentofthis's scoped document set: upgrade particular elements within  a document given this, document, definition, and  localName.
Otherwise, upgrade particular elements within a document given  this, this's relevant global object's associated Document, definition,  localName, and name.

Ifthis's when-defined promise map[name] exists:

Resolve this's when-defined promise map[name] with  constructor.
Remove this's when-defined promise  map[name].
 



Toupgrade particular elements within a document given a  CustomElementRegistry object registry, a Document object  document, a custom element definition definition, a string  localName, and optionally a string name (default localName):

Let upgradeCandidates be all elements that are shadow-including descendantsofdocument, whose custom element registryisregistry,  whose namespace is the HTML namespace, and whose local name is localName,  in shadow-including tree order. Additionally, if name is not  localName, only include elements whose isvalue is equal to name.
For each element elementofupgradeCandidates: enqueue a  custom element upgrade reaction given element and  definition.
 


The get(name) method steps are:

Ifthis's custom element definition set contains an item with name name, then return that  item's constructor.
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:

Ifthis's custom element definition set contains an item with constructor  constructor, then return that item's name.
Return null.
 


The whenDefined(name) method  steps are:

Ifname is not a valid custom element name, then return a  promise rejected witha"SyntaxError"  DOMException.
Ifthis's custom element definition set contains an item with name name, then return a  promise resolved with that item's constructor.
Ifthis's when-defined promise map[name] does not  exist, then set  this's when-defined promise map[name] to a new  promise.
Return this's when-defined promise  map[name].
 


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(root) method steps  are:


For each shadow-including inclusive descendant candidateofroot, in shadow-including tree order:

Ifcandidate is not an Element node, then  continue.
Ifcandidate's custom element  registry is not this, then continue.
Try to upgrade candidate.
 



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.
 
constel= document.createElement("spider-man");

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

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

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



The initialize(root) method  steps are:

Ifthis's is scoped is false and either root is a  Document node or root's node document's custom element registry is not  this, then throw a "NotSupportedError"  DOMException.
Ifroot is a Document node whose custom element registry is null, then set  root's custom element registrytothis.
Otherwise, if root is a ShadowRoot node whose custom element registry is null, then set  root's custom element  registrytothis.

For each inclusive descendant inclusiveDescendantofroot,  in tree order:

IfinclusiveDescendant is not an Element node, then  continue.

IfinclusiveDescendant's custom  element registry is null:

Set inclusiveDescendant's custom element registrytothis.
Ifthis's is scoped is true, then append inclusiveDescendant's node documenttothis's scoped document set.

IfinclusiveDescendant's custom element registry is not  this, then continue.
Try to upgrade  inclusiveDescendant.
 


Once the custom element registry of a node is initialized to a  CustomElementRegistry object, it intentionally cannot be changed any further. This  simplifies reasoning about code and allows implementations to optimize.

4.13.5 Upgrades



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


Ifelement'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();

    constb= 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.
 

Set element's custom  element definitiontodefinition.

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.

For each attributeinelement's attribute list, in  order, enqueue a custom element callback reaction with element, callback  name "attributeChangedCallback", and « attribute's local name,  null, attribute's value, attribute's namespace ».
Ifelementisconnected, then enqueue a custom element  callback reaction with element, callback name "connectedCallback", and « ».
Add element to the end of definition's construction stack.
Let Cbedefinition's constructor.
Set the surrounding agent's active custom  element constructor map[C] to element's custom element registry.

Run the following steps while catching any exceptions:


Ifdefinition'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.

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

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

IfCnon-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 Cfinishes and control returns to this algorithm. Otherwise, they  will execute after Cand the rest of the upgrade process finishes.


IfSameValue(constructResult, element) is false, then  throw a TypeError.

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

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


Remove the surrounding agent's active  custom element constructor map[C].

This is a no-op if Cimmediately calls super() as it ought to do.


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


Assuming Ccalls 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.)

IfCdoes 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:

Set element's custom  element definition to null.
Empty element's custom element reaction queue.

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


Ifelement is a form-associated custom element, then:

Reset the form ownerofelement. If element is  associated with a form element, then  enqueue a custom element callback reaction with element, callback  name "formAssociatedCallback", and « the associated form  ».
Ifelementisdisabled, then  enqueue a custom element callback reaction with element, callback name  "formDisabledCallback", and « true ».

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


Totry to upgrade an element given an element  element:

Let definition be the result of looking up a custom element definition given element's custom element registry, element's  namespace, element's local name, and element's isvalue.
Ifdefinition is not null, then enqueue a custom element upgrade  reaction given element and definition.
 

4.13.6 Custom element reactions



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

When upgraded, its constructor is run, with no arguments.
When it becomes connected, its connectedCallback is  called, with no arguments.
When it becomes disconnected, its disconnectedCallback  is called, with no arguments.
When it is moved, its connectedMoveCallback is called, with no arguments.
When it is adopted into a new document, its adoptedCallback is called, given the old document and new document as  arguments.
When any of its attributes are changed, appended, removed, or replaced, its attributeChangedCallback is called, given the attribute's local name, old value,  new value, and namespace as arguments. (An attribute's old or new value is considered to be null  when the attribute is added or removed, respectively.)
When the user agent resets the form owner of a  form-associated custom element and doing so changes the form owner, its formAssociatedCallback is called, given the new form owner (or null if no owner)  as an argument.
When the form owner of a form-associated custom elementisreset, its formResetCallback is  called.
When the disabled state of a  form-associated custom element is changed, its formDisabledCallback is called, given the new state as an  argument.
When the user agent updates a form-associated custom element's value on behalf of  a user or as part of navigation, its formStateRestoreCallback is called, given the new state and a string indicating  a reason, "autocomplete" or "restore", as  arguments.

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 is 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:

Anupgrade reaction, which will upgrade the custom element and contains a custom  element definition; or
Acallback reaction, which will call a lifecycle callback, and contains a  callback function as well as a list of arguments.

This is all summarized in the following schematic diagram:

, then , then ). 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)." style="width: 80%; max-width: 580px;">


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

Let reactionsStackbeelement's relevant agent's  custom element reactions stack.

IfreactionsStack is empty, then:

Add elementtoreactionsStack's backup element  queue.
IfreactionsStack's processing the backup element queue flag is  set, then return.
Set reactionsStack's processing the backup element queue  flag.

Queue a microtask to perform the following steps:

Invoke custom element reactionsinreactionsStack's  backup element queue.
Unset reactionsStack's processing the backup element queue  flag.
 

Otherwise, add elementtoelement's relevant agent's  current element queue.
 


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

Let definitionbeelement's custom element definition.

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

IfcallbackName is "connectedMoveCallback" and  callback is null:  
Let disconnectedCallback be the value of the entry in definition's  lifecycle callbacks  with key "disconnectedCallback".
Let connectedCallback be the value of the entry in definition's  lifecycle callbacks  with key "connectedCallback".
IfconnectedCallback and disconnectedCallback are null, then  return.

Set callback to the following steps:

IfdisconnectedCallback is not null, then call  disconnectedCallback with no arguments.
IfconnectedCallback is not null, then call connectedCallback  with no arguments.
 

Ifcallback is null, then return.

IfcallbackName is "attributeChangedCallback":

Let attributeName be the first element of args.
Ifdefinition's observed attributes does  not contain attributeName, then return.

Add a new callback reactiontoelement's custom element  reaction queue, with callback function callback and arguments  args.
Enqueue an element on the appropriate element queue given  element.
 


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

Add a new upgrade reactiontoelement's custom element  reaction queue, with custom element definition  definition.
Enqueue an element on the appropriate element queue given  element.
 


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


While queue is not empty:

Let element be the result of dequeuing  from queue.
Let reactionsbeelement's custom element reaction  queue.

Repeat until reactions is empty:


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.

If this throws an exception, catch it, and report it for reaction's custom element  definition's constructor's corresponding  JavaScript object's associated realm's global object.

callback reaction

Invoke reaction's  callback function with reaction's arguments and "report", and callback this value  set to element.
 






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:

Push a new element queue onto this object's  relevant agent's custom element reactions stack.
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.
Let queue be the result of popping from this  object's relevant agent's custom element reactions stack.
Invoke custom element reactionsinqueue.
If an exception exception was thrown by the original steps, rethrow  exception.
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 reactionorenqueuing 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:

HTMLInputElement's webkitdirectory and incremental IDL attributes
HTMLLinkElement's scope IDL attribute
 

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 (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:

Ifthis's is value is not null, then throw a "NotSupportedError"  DOMException.
Let definition be the result of looking up a custom element definition given this's custom element registry, this's  namespace, this's local name, and null.
Ifdefinition is null, then throw a  "NotSupportedError" DOMException.
Ifdefinition's disable internals is true,  then throw a "NotSupportedError" DOMException.
Ifthis's attached internals is non-null, then throw a  "NotSupportedError" DOMException.
Ifthis's custom element state is not "precustomized" or "custom", then throw a  "NotSupportedError" DOMException.
Set this's attached internals to a new  ElementInternals instance whose target  elementisthis.
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;
};

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.


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:

Let targetbethis's target  element.
Iftarget is not a shadow host, then return null.
Let shadowbetarget's shadow root.
Ifshadow's available to element internals is false, then return  null.
Return shadow.
 
4.13.7.3 Form-associated custom elements

internals.setFormValue(value)

Sets both the state and submission valueofinternals's target elementtovalue.

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

internals.setFormValue(value,  state)

Sets the submission valueofinternals's target elementtovalue, and its statetostate.

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

internals.form
Returns the form ownerofinternals'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 elementassatisfying 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 listofentries.

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  listofentries.

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 « the state to be restored, "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 « the state  value determined by history of state value and some heuristics, "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".


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:

Let elementbethis's target  element.
Ifelement is not a form-associated custom element, then throw a  "NotSupportedError" DOMException.
Set element's submission valuetovalueifvalue is  not a FormData object, or to a cloneofvalue's entry list otherwise.
If the state argument of the function is omitted, set element's  state to its submission  value.
Otherwise, if state is a FormData object, set element's  state to a cloneofstate's entry list.
Otherwise, set element's statetostate.
 


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:

Let elementbethis's target  element.
Ifelement is not a form-associated custom element, then throw a  "NotSupportedError" DOMException.
Ifflags contains one or more true values and message is not given  or is the empty string, then throw a TypeError.
For each entry flag  valueofflags, set  element's validity flag with the name flagtovalue.
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.
Ifelement's customError validity flag is true, then set  element's custom validity error messagetoelement's  validation message. Otherwise, set  element's custom validity error message to the empty string.
Ifanchor is not given, then set it to element.
Otherwise, if anchor is not a shadow-including inclusive  descendantofelement, then throw a "NotFoundError"  DOMException.
Set element's validation anchortoanchor.
 


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:

Let elementbethis's target  element.
Ifelement is not a form-associated custom element, then throw a  "NotSupportedError" DOMException.
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:


Ifelement's submission value is a  listofentries, then append each item of element's submission valuetoentry 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.

If the element does not have a name attribute  specified, or its name attribute's value is the empty string,  then return.
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 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 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 stateNameofinternals.states)
for (const stateNameofinternals.states.entries())
for (const stateNameofinternals.states.keys())
for (const stateNameofinternals.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");