Edition for Web Developers — Last Updated 12 December 2024
CustomElementRegistry
interfaceSupport in all current engines.
カスタム要素は、著者に完全に機能するDOM要素を独自に構築する方法を提供する。スクリプティングなどで事後に追加されたアプリケーション固有の動作とともに、著者は文書内で非標準要素を常に使用することができたが、そのような要素は歴史的に不適合であり、あまり機能しなかった。カスタム要素を定義することで、著者はパーサーに要素を正しく構成する方法と、そのクラスの要素が変更にどのように反応するかを通知できる。
カスタム要素は、(カスタム要素の定義のような)低レベルの著者公開拡張ポイントに関して、(HTMLの要素のような)既存のプラットフォーム機能を説明することによって、"プラットフォームを合理化する"ための大きな努力の一部である。今日、HTMLの既存要素の振る舞いを完全に説明することを妨げるカスタム要素(機能的および意味的な要素)の機能には多くの制限があるが、我々は時間の経過とともにこのギャップを縮小したいと思う。
自律カスタム要素を作成する方法を説明するために、国旗の小さなアイコンのレンダリングをカプセル化するカスタム要素を定義してみよう。私たちの目標は、次のように使用できるようにすることである:
< flag-icon country = "nl" ></ flag-icon >
これを行うには、まずカスタム要素のクラスを宣言し、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.
}
}
次に、要素を定義するために次のクラスを使用して必要がある:
customElements. define( "flag-icon" , FlagIcon);
この時点で、上記のコードは動作する。flag-icon
タグを見るたびに、パーサーはFlagIcon
クラスの新しいインスタンスを作成し、要素の内部状態を設定し、レンダリングを更新するために使用する(適切な場合)、新しいcountry
属性についてコードに伝える。
DOM APIを使用してflag-icon
要素を作成することもできる:
const flagIcon = document. createElement( "flag-icon" )
flagIcon. country = "jp"
document. body. appendChild( flagIcon)
最後に、カスタム要素コンストラクタ自体を使用することもできる。つまり、上記のコードは次のコードと同じである:
const flagIcon = new FlagIcon()
flagIcon. country = "jp"
document. body. appendChild( flagIcon)
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.
カスタマイズされた組み込み要素は、自律カスタム要素とはわずかに異なる定義があり、そして大きく異なる使われ方をする、独自の種類のカスタム要素である。この要素は、新しいカスタム機能でHTMLの既存要素を拡張することで、HTMLの既存要素由来の動作の再利用を可能にするために存在する。残念なことに、HTML要素の既存の動作の多くが自律カスタム要素を純粋に使用して複製できないため、これは重要である。代わりに、カスタマイズされた組み込み要素は、既存の要素にカスタム構築動作、ライフサイクルフック、およびプロトタイプチェーンをインストールを可能にし、既存の要素の上にこれらの機能を本質的に"混在"させる。
カスタマイズされた組み込み要素は、ユーザーエージェントや他のソフトウェアが要素のセマンティクスおよび動作を識別するために要素のローカル名として扱うので、自律カスタム要素とは異なる構文を必要とする。つまり、既存の動作の上に構築するカスタマイズされた組み込み要素の概念は、元のローカル名を保持する拡張要素に決定的に依存する。
この例において、plastic-button
というカスタマイズされた組み込み要素を作成する。これは通常のボタンのように動作するが、クリックするたびにファンシーなアニメーション効果が追加される。今回はHTMLElement
の代わりにHTMLButtonElement
を拡張するが、前と同じようにクラスを定義することから始める:
class PlasticButton extends HTMLButtonElement {
constructor() {
super ();
this . addEventListener( "click" , () => {
// Draw some fancy animation effects!
});
}
}
カスタム要素を定義する場合、extends
オプションも指定する必要がある:
customElements. define( "plastic-button" , PlasticButton, { extends : "button" });
一般に、拡張されている要素の名前は、同じインターフェイスを共有する要素(HTMLQuoteElement
を共有するq
とblockquote
の両者など)を共有するため、拡張する要素インターフェイスを単に調べるだけでは判断することができない。
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 >
カスタマイズされた組み込み要素を自律カスタム要素として使用しようとするとうまくいかない。つまり、<plastic-button>Click me?</plastic-button>
は特別な動作をしないHTMLElement
を作成するだけである。
プログラムでカスタマイズしたビルトイン要素を作成する必要がある場合、次のcreateElement()
の形式を使用することができる:
const plasticButton = document. createElement( "button" , { is: "plastic-button" });
plasticButton. textContent = "Click me!" ;
前と同じように、コンストラクタも動作する:
const plasticButton2 = new PlasticButton();
console. log( plasticButton2. localName); // will output "button"
console. assert( plasticButton2 instanceof PlasticButton);
console. assert( plasticButton2 instanceof HTMLButtonElement);
プログラムでカスタマイズした組み込み要素を作成する場合、明示的に設定されていないのでis
属性はDOMに存在しない。ただし、シリアライズ時に出力に追加される。
console. assert( ! plasticButton. hasAttribute( "is" ));
console. log( plasticButton. outerHTML); // will output '<button is="plastic-button"></button>'
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.
カスタマイズされた組み込み要素は、有用なユーザーエージェントが提供する動作またはAPIを持つ既存のHTML要素を拡張を可能にするように設計されている。そのため、この仕様で定義される既存のHTML要素のみを拡張することができ、要素インターフェイスとしてHTMLUnknownElement
を使用するように定義されたbgsound
、blink
、isindex
、keygen
、multicol
、nextid
、またはspacer
などのレガシー要素は拡張できない。
この要件の1つの理由は将来の互換性である。もしカスタマイズされた組み込み要素が、たとえばcombobox
のような、現在不明の要素を拡張したものが定義されたなら、派生したカスタマイズされた組み込み要素のコンシューマーが、ユーザーエージェント提供の動作に興味を持たない基本要素に依存するようになるため、これはこの仕様が将来combobox
要素を定義することを妨げるかもしれない。
以下で規定されているように、そして上記で触れたように、単にtaco-button
という要素を定義して使用しても、そのような要素がボタンを表すことを意味しない。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.
自律カスタム要素を依然として使用する一方で、様々なユーザーに所望のボタンセマンティクスを伝えるためためには、複数の技術を用いる必要がある:
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. この場合、最も関連性の高いイベントハンドラーは、適切なkeydown
イベントをclick
イベントとしてプロキシするものであるので、キーボードとクリックの両方でボタンをアクティブにすることができる。
taco-button
要素に提供される任意のデフォルトの視覚スタイリングに加えて、視覚スタイリングはまた、例えば無効になるなど、論理状態の変化を反映するように更新される必要がある。つまり、taco-button
の規則を持つスタイルシートはすべて、taco-button[disabled]
の規則も必要となる。
これらの点を念頭に置いて、(無効にする機能を含む)ボタンのセマンティクスを伝える責任を負ったフル機能のtaco-button
は、次のようになる:
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).
対照的に、前節で示したように、単一のカスタマイズされた組み込み要素は、button
要素のセマンティクスと動作を自動的に継承する。これらの動作を手動で実装する必要はない。一般に、HTMLの既存要素の上に構築される重要な動作やセマンティクスを持つ要素については、カスタマイズされた組み込み要素の開発、保守、および消費が容易になる。
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.
Autonomous custom elements have the following element definition:
is
attributeform
, for form-associated custom elements — Associates the element with a form
elementdisabled
, for form-associated custom elements — Whether the form control is disabledreadonly
, for form-associated custom elements — Affects willValidate
, plus any behavior added by the custom element authorname
, for form-associated custom elements — Name of the element to use for form submission and in the form.elements
APIHTMLElement
)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-😍>
.
CustomElementRegistry
interfaceSupport in all current engines.
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.
window.customElements.define(name, constructor)
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)
window.customElements.getName(constructor)
window.customElements.whenDefined(name)
SyntaxError
" DOMException
if not given a valid custom element name.window.customElements.upgrade(root)
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 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.
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.shadowRoot
Returns 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.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"
.
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.size
Returns 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" );