Living Standard — Last Updated 17 May 2024
CustomElementRegistry
interfaceSupport in all current engines.
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 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.
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
)
This section is non-normative.
Adding a static formAssociated
property, with a true value, makes an autonomous custom element a form-associated custom element. The ElementInternals
interface helps you to implement functions and properties common to form control elements.
class
MyCheckbox
extends
HTMLElement
{
static
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
>
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.
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 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.
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.
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
>
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
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, as it is saved on the element as its is
value.
Autonomous custom elements have the following element definition:
is
attribute
form
, for form-associated custom elements — Associates the element with a form
element
disabled
, for form-associated custom elements — Whether the form control is disabled
readonly
, for form-associated custom elements — Affects willValidate
, plus any behavior added by the custom element author
name
, for form-associated custom elements — Name of the element to use for form submission and in the form.elements
API
HTMLElement
)
An autonomous custom element does not have any special meaning: it represents its children. A customized built-in element inherits the semantics of the element that it extends.
Any namespace-less attribute that is relevant to the element's functioning, as determined by the element's author, may be specified on an autonomous custom element, so long as the attribute name is XML-compatible and contains no ASCII upper alphas. The exception is the is
attribute, which must not be specified on an autonomous custom element (and which will have no effect if it is).
Customized built-in elements follow the normal requirements for attributes, based on the elements they extend. To add custom attribute-based behavior, use data-*
attributes.
An autonomous custom element is called a form-associated custom element if the element is associated with a custom element definition whose form-associated field is set to true.
The name
attribute represents the form-associated custom element's name. The disabled
attribute is used to make the form-associated custom element non-interactive and to prevent its submission value from being submitted. The form
attribute is used to explicitly associate the form-associated custom element with its form owner.
The readonly
attribute of form-associated custom elements specifies that the element is barred from constraint validation. User agents don't provide any other behavior for the attribute, but custom element authors should, where possible, use its presence to make their control non-editable in some appropriate fashion, similar to the behavior for the readonly attribute on built-in form controls.
Constraint validation: If the readonly
attribute is specified on a form-associated custom element, the element is barred from constraint validation.
The reset algorithm for form-associated custom elements is to enqueue a custom element callback reaction with the element, callback name "formResetCallback
", and an empty argument list.
A valid custom element name is a sequence of characters name that meets all of the following requirements:
name must match the PotentialCustomElementName
production:
PotentialCustomElementName ::=
[a-z] (PCENChar)* '-' (PCENChar)*
PCENChar ::=
"-" | "." | [0-9] | "_" | [a-z] | #xB7 | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x203F-#x2040] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
This uses the EBNF notation from the XML specification. [XML]
name must not be any 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]
These requirements ensure a number of goals for valid custom element names:
They start with an ASCII lower alpha, ensuring that the HTML parser will treat them as tags instead of as text.
They do not contain any ASCII upper alphas, ensuring that the user agent can always treat HTML elements ASCII-case-insensitively.
They contain a hyphen, used for namespacing and to ensure forward compatibility (since no elements will be added to HTML, SVG, or MathML with hyphen-containing local names in the future).
They can always be created with createElement()
and createElementNS()
, which have restrictions that go beyond the parser's.
Apart from these restrictions, a large variety of names is allowed, to give maximum flexibility for use cases like <math-α>
or <emotion-😍>
.
A custom element definition describes a custom element and consists of:
CustomElementConstructor
callback function type value wrapping the custom element constructor
sequence<DOMString>
connectedCallback
", "disconnectedCallback
", "adoptedCallback
", "attributeChangedCallback
", "formAssociatedCallback
", "formDisabledCallback
", "formResetCallback
", and "formStateRestoreCallback
". The corresponding values are either a Web IDL Function
callback function type value, or null. By default the value of each entry is null.
attachInternals()
.
attachShadow()
.
To look up a custom element definition, given a document, namespace, localName, and is, perform the following steps. They will return either a custom element definition or null:
If namespace is not the HTML namespace, return null.
If document's browsing context is null, return null.
Let registry be document's relevant global object's CustomElementRegistry
object.
If there is custom element definition in registry with name and local name both equal to localName, return that custom element definition.
If there is a custom element definition in registry with name equal to is and local name equal to localName, return that custom element definition.
Return null.
CustomElementRegistry
interfaceSupport in all current engines.
Each Window
object is associated with a unique instance of a CustomElementRegistry
object, allocated when the Window
object is created.
Custom element registries are associated with Window
objects, instead of Document
objects, since each custom element constructor inherits from the HTMLElement
interface, and there is exactly one HTMLElement
interface per Window
object.
Support in all current engines.
The customElements
attribute of the Window
interface must return the CustomElementRegistry
object for that Window
object.
[
Exposed
=
Window
]
interface
CustomElementRegistry
{
[
CEReactions
]
undefined
define
(
DOMString
name
,
CustomElementConstructor
constructor
,
optional
ElementDefinitionOptions
options
= {});
(
CustomElementConstructor
or
undefined
)
get
(
DOMString
name
);
DOMString
?
getName
(
CustomElementConstructor
constructor
);
Promise
<
CustomElementConstructor
>
whenDefined
(
DOMString
name
);
[
CEReactions
]
undefined
upgrade
(
Node
root
);
};
callback
CustomElementConstructor
=
HTMLElement
();
dictionary
ElementDefinitionOptions
{
DOMString
extends
;
};
Every CustomElementRegistry
has a set of custom element definitions, initially empty. In general, algorithms in this specification look up elements in the registry by any of name, local name, or constructor.
Every CustomElementRegistry
also has an element definition is running flag which is used to prevent reentrant invocations of element definition. It is initially unset.
Every CustomElementRegistry
also has a when-defined promise map, mapping valid custom element names to promises. It is used to implement the whenDefined()
method.
window.customElements.define(name, constructor)
Support in all current engines.
window.customElements.define(name, constructor, { extends: baseLocalName })
NotSupportedError
" DOMException
will be thrown upon trying to extend a custom element or an unknown element.
window.customElements.get(name)
Support in all current engines.
window.customElements.getName(constructor)
window.customElements.whenDefined(name)
CustomElementRegistry/whenDefined
Support in all current engines.
SyntaxError
" DOMException
if not given a valid custom element name.
window.customElements.upgrade(root)
Support in all current engines.
Element definition is a process of adding a custom element definition to the CustomElementRegistry
. This is accomplished by the define()
method. When invoked, the define(name, constructor, options)
method must run these steps:
If IsConstructor(constructor) is false, then throw a TypeError
.
If name is not a valid custom element name, then throw a "SyntaxError
" DOMException
.
If this CustomElementRegistry
contains an entry with name name, then throw a "NotSupportedError
" DOMException
.
If this CustomElementRegistry
contains an entry with constructor constructor, then throw a "NotSupportedError
" DOMException
.
Let localName be name.
Let extends be the value of the extends
member of options, or null if no such member exists.
If extends is not null, then:
If extends is a valid custom element name, then throw a "NotSupportedError
" DOMException
.
If the element interface for extends and the HTML namespace is HTMLUnknownElement
(e.g., if extends does not indicate an element definition in this specification), then throw a "NotSupportedError
" DOMException
.
Set localName to extends.
If this CustomElementRegistry
's element definition is running flag is set, then throw a "NotSupportedError
" DOMException
.
Set this CustomElementRegistry
's element definition is running flag.
Let formAssociated be false.
Let disableInternals be false.
Let disableShadow be false.
Let observedAttributes be an empty sequence<DOMString>
.
Run the following substeps while catching any exceptions:
Let prototype be ? Get(constructor, "prototype").
If Type(prototype) is not Object, then throw a TypeError
exception.
Let lifecycleCallbacks be a map with the keys "connectedCallback
", "disconnectedCallback
", "adoptedCallback
", and "attributeChangedCallback
", each of which belongs to an entry whose value is null.
For each of the keys callbackName in lifecycleCallbacks, in the order listed in the previous step:
Let callbackValue be ? Get(prototype, callbackName).
If callbackValue is not undefined, then set the value of the entry in lifecycleCallbacks with key callbackName to the result of converting callbackValue to the Web IDL Function
callback type. Rethrow any exceptions from the conversion.
If the value of the entry in lifecycleCallbacks with key "attributeChangedCallback
" is not null, then:
Let observedAttributesIterable be ? Get(constructor, "observedAttributes").
If observedAttributesIterable 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").
If disabledFeaturesIterable is not undefined, then set disabledFeatures to the result of converting disabledFeaturesIterable to a sequence<DOMString>
. Rethrow any exceptions from the conversion.
Set disableInternals to true if disabledFeatures contains "internals
".
Set disableShadow to true if disabledFeatures contains "shadow
".
Let formAssociatedValue be ? Get( constructor, "formAssociated").
Set formAssociated to the result of converting formAssociatedValue to a boolean
. Rethrow any exceptions from the conversion.
If formAssociated is true, for each of "formAssociatedCallback
", "formResetCallback
", "formDisabledCallback
", and "formStateRestoreCallback
" callbackName:
Let callbackValue be ? Get(prototype, callbackName).
If callbackValue is not undefined, then set the value of the entry in lifecycleCallbacks with key callbackName to the result of converting callbackValue to the Web IDL Function
callback type. Rethrow any exceptions from the conversion.
Then, perform the following substep, regardless of whether the above steps threw an exception or not:
Unset this CustomElementRegistry
's element definition is running flag.
Finally, if the first set of substeps threw an exception, then rethrow that exception (thus terminating this algorithm). Otherwise, continue onward.
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.
Add definition to this CustomElementRegistry
.
Let document be this CustomElementRegistry
's relevant global object's associated Document
.
Let upgrade candidates be all elements that are shadow-including descendants of document, whose namespace is the HTML namespace and whose local name is localName, in shadow-including tree order. Additionally, if extends is non-null, only include elements whose is
value is equal to name.
For each element element in upgrade candidates, enqueue a custom element upgrade reaction given element and definition.
If this CustomElementRegistry
's when-defined promise map contains an entry with key name:
Let promise be the value of that entry.
Resolve promise with constructor.
Delete the entry with key name from this CustomElementRegistry
's when-defined promise map.
When invoked, the get(name)
method must run these steps:
If this CustomElementRegistry
contains an entry with name name, then return that entry's constructor.
Otherwise, return undefined.
The getName(constructor)
method steps are:
If this CustomElementRegistry
contains an entry with constructor constructor, then return that entry's name.
Return null.
When invoked, the whenDefined(name)
method must run these steps:
If name is not a valid custom element name, then return a promise rejected with a "SyntaxError
" DOMException
.
If this CustomElementRegistry
contains an entry with name name, then return a promise resolved with that entry's constructor.
Let map be this CustomElementRegistry
's when-defined promise map.
If map does not contain an entry with key name, create an entry in map with key name and whose value is a new promise.
Let promise be the value of the entry in map with key name.
Return promise.
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
;
});
When invoked, the upgrade(root)
method must run these steps:
Let candidates be a list of all of root's shadow-including inclusive descendant elements, in shadow-including tree order.
For each candidate of candidates, 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.
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!
To upgrade an element, given as input a custom element definition definition and an element element, run the following steps:
If element's custom element state is not "undefined
" or "uncustomized
", then return.
One scenario where this can occur due to reentrant invocation of this algorithm, as in the following example:
<!DOCTYPE html>
<
x-foo
id
=
"a"
></
x-foo
>
<
x-foo
id
=
"b"
></
x-foo
>
<
script
>
// Defining enqueues upgrade reactions for both "a" and "b"
customElements
.
define
(
"x-foo"
,
class
extends
HTMLElement
{
constructor
()
{
super
();
const
b
=
document
.
querySelector
(
"#b"
);
b
.
remove
();
// While this constructor is running for "a", "b" is still
// undefined, and so inserting it into the document will enqueue a
// second upgrade reaction for "b" in addition to the one enqueued
// by defining x-foo.
document
.
body
.
appendChild
(
b
);
}
})
</
script
>
This step will thus bail out the algorithm early when upgrade an element is invoked with "b
" a second time.
Set element's custom element definition to definition.
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 attribute in element's attribute list, in order, enqueue a custom element callback reaction with element, callback name "attributeChangedCallback
", and an argument list containing attribute's local name, null, attribute's value, and attribute's namespace.
If element is connected, then enqueue a custom element callback reaction with element, callback name "connectedCallback
", and an empty argument list.
Add element to the end of definition's construction stack.
Let C be definition's constructor.
Run the following substeps while catching any exceptions:
If definition's disable shadow is true and element's shadow root is non-null, then throw a "NotSupportedError
" DOMException
.
This is needed as attachShadow()
does not use look up a custom element definition while attachInternals()
does.
Set element's custom element state to "precustomized
".
Let constructResult be the result of constructing C, with no arguments.
If C non-conformantly uses an API decorated with the [CEReactions]
extended attribute, then the reactions enqueued at the beginning of this algorithm will execute during this step, before C finishes and control returns to this algorithm. Otherwise, they will execute after C and the rest of the upgrade process finishes.
If SameValue(constructResult, element) is false, then throw a TypeError
.
This can occur if C constructs another instance of the same custom element before calling super()
, or if C uses JavaScript's return
-override feature to return an arbitrary HTMLElement
object from the constructor.
Then, perform the following substep, regardless of whether the above steps threw an exception or not:
Remove the last entry from the end of definition's construction stack.
Assuming C calls super()
(as it will if it is conformant), and that the call succeeds, this will be the already constructed marker that replaced the element we pushed at the beginning of this algorithm. (The HTML element constructor carries out this replacement.)
If C does not call super()
(i.e. it is not conformant), or if any step in the HTML element constructor throws, then this entry will still be element.
Finally, if the above steps threw an exception, then:
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
".
If element is a form-associated custom element, then:
Reset the form owner of element. If element is associated with a form
element, then enqueue a custom element callback reaction with element, callback name "formAssociatedCallback
", and « the associated form
».
If element is disabled, then enqueue a custom element callback reaction with element, callback name "formDisabledCallback
" and « true ».
Set element's custom element state to "custom
".
To try to upgrade an element, given as input an element element, run the following steps:
Let definition be the result of looking up a custom element definition given element's node document, element's namespace, element's local name, and element's is
value.
If definition is not null, then enqueue a custom element upgrade reaction given element and definition.
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 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 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 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:
An upgrade reaction, which will upgrade the custom element and contains a custom element definition; or
A callback 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:
To enqueue an element on the appropriate element queue, given an element element, run the following steps:
Let reactionsStack be element's relevant agent's custom element reactions stack.
If reactionsStack is empty, then:
Add element to reactionsStack's backup element queue.
If reactionsStack'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 reactions in reactionsStack's backup element queue.
Unset reactionsStack's processing the backup element queue flag.
Otherwise, add element to element's relevant agent's current element queue.
To enqueue a custom element callback reaction, given a custom element element, a callback name callbackName, and a list of arguments args, run the following steps:
Let definition be element's custom element definition.
Let callback be the value of the entry in definition's lifecycle callbacks with key callbackName.
If callback is null, then return.
If callbackName is "attributeChangedCallback
", then:
Let attributeName be the first element of args.
If definition's observed attributes does not contain attributeName, then return.
Add a new callback reaction to element's custom element reaction queue, with callback function callback and arguments args.
Enqueue an element on the appropriate element queue given element.
To enqueue a custom element upgrade reaction, given an element element and custom element definition definition, run the following steps:
Add a new upgrade reaction to element's custom element reaction queue, with custom element definition definition.
Enqueue an element on the appropriate element queue given element.
To invoke 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 reactions be element'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:
If this throws an exception, catch it, and report the exception.
To ensure custom element reactions are triggered appropriately, we introduce the [CEReactions]
IDL extended attribute. It indicates that the relevant algorithm is to be supplemented with additional steps in order to appropriately track and invoke custom element reactions.
The [CEReactions]
extended attribute must take no arguments, and must not appear on anything other than an operation, attribute, setter, or deleter. Additionally, it must not appear on readonly attributes.
Operations, attributes, setters, or deleters annotated with the [CEReactions]
extended attribute must run the following steps in place of the ones specified in their description:
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 reactions in queue.
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 reaction or enqueuing a custom element upgrade reaction, for example by modifying any attributes or child elements, must also be decorated with the [CEReactions]
attribute.
As of the time of this writing, the following nonstandard or not-yet-standardized APIs are known to fall into this category:
HTMLInputElement
's webkitdirectory
and incremental
IDL attributes
HTMLLinkElement
's scope
IDL attribute
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.
Support in all current engines.
The attachInternals()
method steps are:
If this'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 node document, its namespace, its local name, and null as the is
value.
If definition is null, then throw an "NotSupportedError
" DOMException
.
If definition's disable internals is true, then throw a "NotSupportedError
" DOMException
.
If this's attached internals is non-null, then throw an "NotSupportedError
" DOMException
.
If this'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 element is this.
Return this's attached internals.
ElementInternals
interfaceSupport in all current engines.
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.
internals.shadowRoot
Returns the ShadowRoot
for internals's target element, if the target element is a shadow host, or null otherwise.
Support in all current engines.
The shadowRoot
getter steps are:
Let target be this's target element.
If target is not a shadow host, then return null.
Let shadow be target's shadow root.
If shadow's available to element internals is false, then return null.
Return shadow.
internals.setFormValue(value)
Sets both the state and submission value of internals's target element to value.
If value is null, the element won't participate in form submission.
internals.setFormValue(value, state)
Sets the submission value of internals's target element to value, and its state to state.
If value is null, the element won't participate in form submission.
internals.form
Returns the form owner of internals's target element.
internals.setValidity(flags, message [, anchor ])
Marks internals's target element as suffering from the constraints indicated by the flags argument, and sets the element's validation message to message. If anchor is specified, the user agent might use it to indicate problems with the constraints of internals's target element when the form owner is validated interactively or reportValidity()
is called.
internals.setValidity({})
Marks internals's target element as satisfying its constraints.
internals.willValidate
Returns true if internals's target element will be validated when the form is submitted; false otherwise.
internals.validity
Returns the ValidityState
object for internals's target element.
internals.validationMessage
Returns the error message that would be shown to the user if internals's target element was to be checked for validity.
valid = internals.checkValidity()
Returns true if internals's target element has no validity problems; false otherwise. Fires an invalid
event at the element in the latter case.
valid = internals.reportValidity()
Returns true if internals's target element has no validity problems; otherwise, returns false, fires an invalid
event at the element, and (if the event isn't canceled) reports the problem to the user.
internals.labels
Returns a NodeList
of all the label
elements that internals's target element is associated with.
Each form-associated custom element has submission value. It is used to provide one or more entries on form submission. The initial value of submission value is null, and submission value can be null, a string, a File
, or a list of entries.
Each form-associated custom element has state. It is information with which the user agent can restore a user's input for the element. The initial value of state is null, and state can be null, a string, a File
, or a list of entries.
The setFormValue()
method is used by the custom element author to set the element's submission value and state, thus communicating these to the user agent.
When the user agent believes it is a good idea to restore a form-associated custom element's state, for example after navigation or restarting the user agent, they may enqueue a custom element callback reaction with that element, callback name "formStateRestoreCallback
", an argument list containing the state to be restored, and "restore
".
If the user agent has a form-filling assist feature, then when the feature is invoked, it may enqueue a custom element callback reaction with a form-associated custom element, callback name "formStateRestoreCallback
", an argument list containing the state value determined by history of state value and some heuristics, and "autocomplete
".
In general, the state is information specified by a user, and the submission value is a value after canonicalization or sanitization, suitable for submission to the server. The following examples makes this concrete:
Suppose that we have a form-associated custom element which asks a user to specify a date. The user specifies "3/15/2019", but the control wishes to submit "2019-03-15"
to the server. "3/15/2019" would be a state of the element, and "2019-03-15"
would be a submission value.
Suppose you develop a custom element emulating a the behavior of the existing checkbox input
type. Its submission value would be the value of its value
content attribute, or the string "on"
. Its state would be one of "checked"
, "unchecked"
, "checked/indeterminate"
, or "unchecked/indeterminate"
.
Support in all current engines.
The setFormValue(value, state)
method steps are:
Let element be this's target element.
If element is not a form-associated custom element, then throw a "NotSupportedError
" DOMException
.
Set target element's submission value to value if value is not a FormData
object, or to a clone of value's entry list otherwise.
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 clone of state's entry list.
Otherwise, set element's state to state.
Each form-associated custom element has validity flags named valueMissing
, typeMismatch
, patternMismatch
, tooLong
, tooShort
, rangeUnderflow
, rangeOverflow
, stepMismatch
, and customError
. They are false initially.
Each form-associated custom element has a validation message string. It is the empty string initially.
Each form-associated custom element has a validation anchor element. It is null initially.
Support in all current engines.
The setValidity(flags, message, anchor)
method steps are:
Let element be this's target element.
If element is not a form-associated custom element, then throw a "NotSupportedError
" DOMException
.
If flags contains one or more true values and message is not given or is the empty string, then throw a TypeError
.
For each entry flag → value of flags, set element's validity flag with the name flag to value.
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.
If element's customError
validity flag is true, then set element's custom validity error message to element's validation message. Otherwise, set element's custom validity error message to the empty string.
Set element's validation anchor to null if anchor is not given. Otherwise, if anchor is not a shadow-including descendant of element, then throw a "NotFoundError
" DOMException
. Otherwise, set element's validation anchor to anchor.
ElementInternals/validationMessage
Support in all current engines.
The validationMessage
getter steps are:
Let element be this's target element.
If element 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:
If element's submission value is a list of entries, then append each item of element's submission value to entry list, and return.
In this case, user agent does not refer to the name
content attribute value. An implementation of form-associated custom element is responsible to decide names of entries. They can be the name
content attribute value, they can be strings based on the name
content attribute value, or they can be unrelated to the name
content attribute.
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.
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.
internals.states.add(value)
Adds the string value to the element's states set to be exposed as a pseudo-class.
internals.states.has(value)
Returns true if value is in the element's states set, otherwise false.
internals.states.delete(value)
If the element's states set has value, then it will be removed and true will be returned. Otherwise, false will be returned.
internals.states.clear()
Removes all values from the element's states set.
for (const stateName of internals.states)
for (const stateName of internals.states.entries())
for (const stateName of internals.states.keys())
for (const stateName of internals.states.values())
Iterates over all values in the element's states set.
internals.states.forEach(callback)
Iterates over all values in the element's states set by calling callback once for each value.
internals.states.size
Returns the number of values in the element's states set.
Each custom element has a states set, which is a CustomStateSet
, initially empty.
[
Exposed
=
Window
]
interface
CustomStateSet
{
setlike
<
DOMString
>;
};
The states
getter steps are to return this's target element's states set.
The states set can expose boolean states represented by existence/non-existence of string values. If an author wants to expose a state which can have three values, it can be converted to three exclusive boolean states. For example, a state called readyState
with "loading"
, "interactive"
, and "complete"
values can be mapped to three exclusive boolean states, "loading"
, "interactive"
, and "complete"
:
// Change the readyState from anything to "complete".
this
.
_readyState
=
"complete"
;
this
.
_internals
.
states
.
delete
(
"loading"
);
this
.
_internals
.
states
.
delete
(
"interactive"
);
this
.
_internals
.
states
.
add
(
"complete"
);