Edition for Web Developers — Last Updated 29 October 2025
CustomElementRegistry interfaceSupport in all current engines.
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 elements—both functionally and semantically—that prevent them from fully explaining the behaviors of HTML's existing elements, we hope to shrink this gap over time.
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) 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  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 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 > 
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.
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
  q and blockquote both sharing HTMLQuoteElement).
To construct our customized built-in element from parsed HTML source text, we use
  the is attribute 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 is attribute 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.
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.
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 > 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().
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 > 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.
A return statement must not appear anywhere inside the constructor
   body, unless it is a simple early-return (return or return
   this).
The constructor must not use the document.write()
   or document.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 createElement or createElementNS methods.
In general, work should be deferred to connectedCallback as much as
   possible—especially 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 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(); 
  } 
} A custom 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:
An autonomous custom element, which is defined with no extends option. These types of custom
   elements have a local name equal to their defined name.
A customized built-in element, which is defined with an extends option. These types of custom
   elements have a local name equal to the value passed in their extends option, and their defined name is used as the value of the
   is attribute, which
   therefore must be a valid custom element name.
After a custom element is created,
  changing the value of the is attribute does not
  change the element's behavior.
Autonomous custom elements have the following element definition:
is attributeform, 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 
     HTMLElement)An autonomous custom element does not have any special meaning: it represents its children. A customized built-in element inherits the semantics of the element that it extends.
Any namespace-less attribute that is relevant to the element's functioning, as determined by
  the element's author, may be specified on an autonomous custom element, so long as
  the attribute name is a valid attribute local name and contains no ASCII upper alphas. The exception is the is attribute, which must not be specified on an autonomous custom
  element (and which will have no effect if it is).
Customized built-in elements follow the
  normal requirements for attributes, based on the elements they extend. To add custom
  attribute-based behavior, use data-* attributes.
An autonomous custom element is called a form-associated custom element if the element is associated with a custom element definition whose form-associated field is set to true.
The name attribute represents the form-associated
  custom element's name. The disabled attribute is
  used to make the form-associated custom element non-interactive and to prevent its
  submission value from being submitted. The form attribute is used to explicitly associate the
  form-associated custom element with its form owner.
The readonly attribute of form-associated custom elements specifies that the element is barred
  from constraint validation. User agents don't provide any other behavior for the attribute,
  but custom element authors should, where possible, use its presence to make their control
  non-editable in some appropriate fashion, similar to the behavior for the readonly attribute on built-in form controls.
Constraint validation: If the readonly attribute is specified on a form-associated
  custom element, the element is barred from constraint validation.
The reset algorithm for form-associated custom elements is to enqueue
  a custom element callback reaction with the element, callback name "formResetCallback", and « ».
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-😍>.
CustomElementRegistry interfaceSupport in all current engines.
registry = window.customElementsSupport in all current engines.
Document's CustomElementRegistry
   object.registry = new CustomElementRegistry()CustomElementRegistry object, for scoped usage.registry.define(name,
   constructor)registry.define(name, constructor,
   { extends: baseLocalName })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)registry.getName(constructor)registry.whenDefined(name)SyntaxError" DOMException if not
   given a valid custom element name.registry.upgrade(root)registry.initialize(root)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.Element definition is a process of adding a custom element definition
  to the CustomElementRegistry. This is accomplished by the define() method.
The whenDefined() method can be
   used to avoid performing an action until all appropriate custom
   elements are defined. In this example, we
   combine it with the :defined pseudo-class to hide a
   dynamically-loaded article's contents until we're sure that all of the autonomous custom elements it uses are defined.
articleContainer. hidden =  true ; 
fetch( articleURL) 
  . then( response =>  response. text()) 
  . then( text =>  { 
    articleContainer. innerHTML =  text; 
    return  Promise. all( 
      [... articleContainer. querySelectorAll( ":not(:defined)" )] 
        . map( el =>  customElements. whenDefined( el. localName)) 
    ); 
  }) 
  . then(()  =>  { 
    articleContainer. hidden =  false ; 
  }); The upgrade() method allows upgrading
   of elements at will. Normally elements are automatically upgraded when they become
   connected, but this method can be used if you need to upgrade before you're ready to
   connect the element.
const  el =  document. createElement( "spider-man" ); 
class  SpiderMan extends  HTMLElement {} 
customElements. define( "spider-man" ,  SpiderMan); 
console. assert( ! ( el instanceof  SpiderMan));  // not yet upgraded 
customElements. upgrade( el); 
console. assert( el instanceof  SpiderMan);     // upgraded! A custom 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 element is reset, 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.
It is guaranteed that custom element reactions always are invoked in the same order as their triggering actions, at least within the local context of a single custom element. (Because custom element reaction code can perform its own mutations, it is not possible to give a global ordering guarantee across multiple elements.)
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()Support in all current engines.
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.
internals.shadowRootReturns the ShadowRoot for internals's target element, if the target
   element is a shadow host, or null otherwise.
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.formReturns 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.willValidateReturns true if internals's target element will be validated when the form is submitted; false otherwise.
internals.validityReturns the ValidityState object for internals's target element.
internals.validationMessageReturns 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.labelsReturns 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 « 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".
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.
By using the role and aria* properties of
  ElementInternals, custom element authors can set default accessibile roles, states,
  and property values for their custom element, similar to how native elements behave. See the example above for more details.
internals.states.add(value)Adds the string value to the element's states set to be exposed as a pseudo-class.
internals.states.has(value)Returns true if value is in the element's states set, otherwise false.
internals.states.delete(value)If the element's states set has value, then it will be removed and true will be returned. Otherwise, false will be returned.
internals.states.clear()Removes all values from the element's states set.
for (const stateName of internals.states)for (const stateName of internals.states.entries())for (const stateName of internals.states.keys())for (const stateName of internals.states.values())Iterates over all values in the element's states set.
internals.states.forEach(callback)Iterates over all values in the element's states set by calling callback once for each value.
internals.states.sizeReturns the number of values in the 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" );