动态标准 - 最后更新于 2024 年 9 月 12 日
所有当前引擎都支持。
本节是非规范性的。
自定义元素 为作者提供了一种构建自己的完整功能 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)
本节是非规范性的。
添加一个带有真值的静态 formAssociated
属性,会使 自主的自定义元素 成为 与表单关联的自定义元素。ElementInternals
接口可以帮助您实现表单控件元素的通用函数和属性。
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);
您可以像使用内置的表单关联元素一样使用自定义元素 my-checkbox
。例如,将其放在 form
或 label
中,会将 my-checkbox
元素与其关联,并且提交 form
将发送由 my-checkbox
实现提供的数据。
< form action = "..." method = "..." >
< label >< my-checkbox name = "agreed" ></ my-checkbox > I read the agreement.</ label >
< input type = "submit" >
</ form >
本节是非规范性的。
通过使用 ElementInternals
的适当属性,您的自定义元素可以具有默认的可访问性语义。以下代码扩展了我们上一节中的与表单关联的复选框,以正确设置其默认角色和选中状态,如辅助技术所见
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);
请注意,与内置元素一样,这些只是默认值,可以通过页面作者使用 role
和 aria-*
属性来覆盖。
<!-- 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" >
鼓励自定义元素作者说明其可访问性语义的哪些方面是强大的本机语义,即,不应该被自定义元素的用户覆盖。在我们的示例中,my-checkbox
元素的作者会声明其 role 和 aria-checked
值是强大的本机语义,因此不鼓励使用诸如上面的代码。
本节是非规范性的。
自定义的内置元素 是一种独特的 自定义元素,其定义方式略有不同,使用方式也与 自主的自定义元素 不同。它们的存在是为了允许重用 HTML 现有元素的行为,方法是使用新的自定义功能扩展这些元素。这很重要,因为 HTML 元素的许多现有行为不幸的是无法通过仅仅使用 自主的自定义元素 来复制。相反,自定义的内置元素 允许将自定义构造行为、生命周期挂钩和原型链安装到现有元素上,本质上是在现有元素的基础上“混合”这些功能。
自定义的内置元素 需要与 自主的自定义元素 不同的语法,因为用户代理和其他软件根据元素的本地名称来识别元素的语义和行为。也就是说,自定义的内置元素 在现有行为基础上构建的概念,主要依赖于扩展元素保留其原始本地名称。
在这个例子中,我们将创建一个名为 plastic-button
的 自定义的内置元素,它像一个普通的按钮,但只要点击它就会添加花哨的动画效果。我们首先定义一个类,就像之前一样,不过这次我们扩展的是 HTMLButtonElement
而不是 HTMLElement
class PlasticButton extends HTMLButtonElement {
constructor() {
super ();
this . addEventListener( "click" , () => {
// Draw some fancy animation effects!
});
}
}
在定义自定义元素时,我们还必须指定 extends
选项
customElements. define( "plastic-button" , PlasticButton, { extends : "button" });
通常,扩展的元素名称不能仅仅通过查看它扩展了哪个元素接口来确定,因为许多元素共享同一个接口(例如,q
和 blockquote
都共享 HTMLQuoteElement
)。
为了从解析的 HTML 源文本中构造我们的 自定义的内置元素,我们在 button
元素上使用 is
属性
< 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>'
无论使用哪种方式创建,button
的所有特殊之处也适用于此类“塑料按钮”:它们的焦点行为、参与 表单提交 的能力、disabled
属性等等。
自定义内置元素 旨在允许扩展具有有用用户代理提供的行为或 API 的现有 HTML 元素。 因此,它们只能扩展本规范中定义的现有 HTML 元素,不能扩展旧版元素,例如 bgsound
、blink
、isindex
、keygen
、multicol
、nextid
或 spacer
,这些元素已被定义为使用 HTMLUnknownElement
作为其 元素接口。
对这一要求的原因之一是未来兼容性:如果定义了一个扩展当前未知元素(例如 combobox
)的 自定义内置元素,这将阻止本规范将来定义 combobox
元素,因为派生 自定义内置元素 的使用者将依赖其基本元素没有有趣的用户代理提供的行为。
本节是非规范性的。
正如下面所述,以及上面所提到的,简单地定义和使用一个名为 taco-button
的元素并不意味着这些元素 代表 按钮。 也就是说,像 Web 浏览器、搜索引擎或辅助技术之类的工具不会仅仅根据其定义的名称自动将生成的元素视为按钮。
为了向各种用户传达所需的按钮语义,同时仍然使用 自主自定义元素,需要使用多种技术。
添加 tabindex
属性将使 taco-button
可聚焦。 注意,如果 taco-button
变成逻辑上禁用,则需要删除 tabindex
属性。
添加 ARIA 角色以及各种 ARIA 状态和属性有助于向辅助技术传达语义。 例如,将 角色 设置为 "button
" 将传达该元素是一个按钮的语义,使用户能够使用辅助技术中的常用按钮式交互成功地与控件交互。 设置 aria-label
属性对于为按钮提供一个 可访问名称 是必要的,而不是让辅助技术遍历其子文本节点并宣布它们。 将 aria-disabled
状态设置为 "true
"(当按钮逻辑上被禁用时)会向辅助技术传达按钮的禁用状态。
添加事件处理程序来处理常用的预期按钮行为有助于向 Web 浏览器用户传达按钮的语义。 在这种情况下,最相关的事件处理程序将是一个事件处理程序,它将适当的 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" ;
}
}
}
即使有了这个相当复杂的元素定义,该元素对于使用者来说也不是很好用:它将不断“萌发”它自己意愿的 tabindex
属性,并且它选择 tabindex="0"
可聚焦行为可能与当前平台上的 button
行为不匹配。 这是因为到目前为止,还没有办法为自定义元素指定默认的焦点行为,迫使使用 tabindex
属性来做到这一点(即使它通常用于允许使用者覆盖默认行为)。
相比之下,如上一节所示的简单 自定义内置元素 将自动继承 button
元素的语义和行为,无需手动实现这些行为。 一般来说,对于任何基于 HTML 的现有元素构建的具有非平凡行为和语义的元素,自定义内置元素 将更容易开发、维护和使用。
本节是非规范性的。
由于 元素定义 可以随时发生,非自定义元素可以 创建,然后在注册了适当的 定义 之后,它将成为 自定义元素。 我们将此过程称为从普通元素到自定义元素的“升级”元素。
升级 允许在创建相关元素(例如解析器创建的元素)后,注册 自定义元素定义 的场景。 它们允许对自定义元素中的内容进行渐进增强。 例如,在以下 HTML 文档中,img-viewer
的元素定义是异步加载的
<!DOCTYPE html>
< html lang = "en" >
< title > Image viewer example</ title >
< img-viewer filter = "Kelvin" >
< img src = "images/tree.jpg" alt = "A beautiful tree towering over an empty savannah" >
</ img-viewer >
< script src = "js/elements/img-viewer.js" async ></ script >
这里 img-viewer
元素的定义是使用标记为 async
属性的 script
元素加载的,该元素放置在标记中的 <img-viewer>
标记之后。 在脚本加载时,img-viewer
元素将被视为未定义的元素,类似于 span
。 脚本加载后,它将定义 img-viewer
元素,并且页面上现有的 img-viewer
元素将被升级,应用自定义元素的定义(该定义可能包括应用由字符串“Kelvin”标识的图像过滤器,以增强图像的视觉外观)。
注意,升级 仅适用于文档树中的元素。 (正式地说,已连接 的元素)。 未插入文档的元素将保持未升级。 一个示例说明了这一点
<!DOCTYPE html>
< html lang = "en" >
< title > Upgrade edge-cases example</ title >
< example-element ></ example-element >
< script >
"use strict" ;
const inDocument = document. querySelector( "example-element" );
const outOfDocument = document. createElement( "example-element" );
// Before the element definition, both are HTMLElement:
console. assert( inDocument instanceof HTMLElement);
console. assert( outOfDocument instanceof HTMLElement);
class ExampleElement extends HTMLElement {}
customElements. define( "example-element" , ExampleElement);
// After element definition, the in-document element was upgraded:
console. assert( inDocument instanceof ExampleElement);
console. assert( ! ( outOfDocument instanceof ExampleElement));
document. body. appendChild( outOfDocument);
// Now that we've moved the element into the document, it too was upgraded:
console. assert( outOfDocument instanceof ExampleElement);
</ script >
用户代理提供的内置元素具有一定的状态,这些状态会随着时间的推移而发生变化,具体取决于用户交互和其他因素,并通过 伪类 公开给 Web 作者。 例如,某些表单控件具有“无效”状态,该状态通过 :invalid
伪类 公开。
与内置元素一样,自定义元素 也可能处于各种状态,并且 自定义元素 作者希望以类似于内置元素的方式公开这些状态。
这是通过 :state()
伪类来完成的。 自定义元素作者可以使用 states
属性(ElementInternals
)添加和删除此类自定义状态,这些状态然后作为 :state()
伪类的参数公开。
以下展示了如何使用 :state()
来设置自定义复选框元素的样式。 假设 LabeledCheckbox
不通过内容属性公开其“已选中”状态。
< 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 >
自定义伪类甚至可以定位阴影部分。 上面的示例扩展展示了这一点
< 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 >
在编写 自定义元素构造函数 时,作者受以下一致性要求约束
对 super()
的无参数调用必须是构造函数体中的第一个语句,以便在运行任何其他代码之前建立正确的原型链和 this 值。
return
语句不得出现在构造函数体内的任何位置,除非它是简单的早期返回(return
或 return this
)。
构造函数不得使用 document.write()
或 document.open()
方法。
不得检查元素的属性和子元素,因为在非 升级 情况下,将不存在任何属性和子元素,并且依赖升级会使元素的可用性降低。
元素不得获得任何属性或子元素,因为这违反了使用 createElement
或 createElementNS
方法的消费者的期望。
一般来说,应尽可能将工作推迟到 connectedCallback
中——尤其是涉及获取资源或渲染的工作。 但是,请注意,connectedCallback
可以被调用多次,因此任何真正一次性的初始化工作都需要一个保护措施,以防止它运行两次。
一般来说,构造函数应该用于设置初始状态和默认值,以及设置事件侦听器,并可能设置一个 阴影根。
这些要求中的几个在元素创建期间进行检查,无论是直接还是间接,如果未能遵守这些要求,将导致自定义元素无法通过解析器或DOM API实例化。即使工作是在构造函数启动的微任务中完成,这仍然适用,因为微任务检查点可以在构造后立即发生。
在编写自定义元素反应时,作者应避免操作节点树,因为这会导致意外结果。
元素的connectedCallback
可以在元素断开连接之前排队,但由于回调队列仍在处理,它会导致connectedCallback
针对不再连接的元素。
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
自定义元素是自定义的元素。非正式地说,这意味着它的构造函数和原型由作者定义,而不是由用户代理定义。此作者提供的构造函数称为自定义元素构造函数。
可以定义两种不同类型的自定义元素
自治自定义元素,它是在没有extends
选项的情况下定义的。这些类型的自定义元素的本地名称与其定义名称相同。
自定义内置元素,它是在带有extends
选项的情况下定义的。这些类型的自定义元素的本地名称等于其extends
选项中传递的值,并且它们的定义名称用作is
属性的值,因此必须是有效的自定义元素名称。
在自定义元素创建之后,更改is
属性的值不会改变元素的行为,因为它作为元素的is
值保存。
自治自定义元素具有以下元素定义
is
属性form
,用于与表单关联的自定义元素 - 将元素与form
元素关联disabled
,用于与表单关联的自定义元素 - 表单控件是否被禁用readonly
,用于与表单关联的自定义元素 - 影响willValidate
,以及自定义元素作者添加的任何行为name
,用于与表单关联的自定义元素 - 用于表单提交和form.elements
API的元素名称HTMLElement
)自治自定义元素没有任何特殊含义:它表示其子级。一个自定义内置元素继承了它扩展的元素的语义。
任何对元素功能相关的无命名空间属性,如元素作者所确定,都可以在自治自定义元素上指定,只要属性名称是XML 兼容并且不包含ASCII 大写字母。例外是is
属性,它不能在自治自定义元素上指定(如果指定,它将不起作用)。
自定义内置元素遵循基于其扩展的元素的属性的正常要求。要添加基于属性的自定义行为,请使用data-*
属性。
如果元素与自定义元素定义关联,其中与表单关联字段设置为 true,则自治自定义元素称为与表单关联的自定义元素。
name
属性表示与表单关联的自定义元素的名称。disabled
属性用于使与表单关联的自定义元素不可交互,并防止其提交值被提交。form
属性用于明确地将与表单关联的自定义元素与其表单所有者关联。
与表单关联的自定义元素的readonly
属性指定元素是被禁止进行约束验证的。用户代理不会为该属性提供任何其他行为,但自定义元素作者应尽可能使用其存在使他们的控件以某种适当的方式不可编辑,类似于对内置表单控件的readonly属性的行为。
约束验证:如果在与表单关联的自定义元素上指定了readonly
属性,则该元素是被禁止进行约束验证的。
对于与表单关联的自定义元素,重置算法是将一个自定义元素回调反应排队,其中包含元素、回调名称“formResetCallback
”以及一个空参数列表。
有效的自定义元素名称是满足以下所有要求的字符序列name
name必须匹配PotentialCustomElementName
产生式
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]
name不能是以下任何一个
annotation-xml
color-profile
font-face
font-face-src
font-face-uri
font-face-format
font-face-name
missing-glyph
上面的名称列表是来自适用规范(即SVG 2和MathML)的所有包含连字符的元素名称的摘要。[SVG] [MATHML]
这些要求确保了有效的自定义元素名称的几个目标
它们以ASCII 小写字母开头,确保 HTML 解析器会将它们视为标签,而不是文本。
它们不包含任何ASCII 大写字母,确保用户代理始终可以对 HTML 元素进行 ASCII 不区分大小写处理。
它们包含一个连字符,用于命名空间并确保向前兼容性(因为将来不会在 HTML、SVG 或 MathML 中添加包含连字符的本地名称的元素)。
它们始终可以使用 createElement()
和 createElementNS()
创建,它们有超出解析器范围的限制。
除了这些限制外,还允许使用各种名称,以最大程度地灵活地用于诸如 <math-α>
或 <emotion-😍>
之类的用例。
一个 自定义元素定义 描述了一个 自定义元素,它包括
CustomElementConstructor
回调函数类型值,包装了 自定义元素构造函数sequence<DOMString>
connectedCallback
"、"disconnectedCallback
"、"adoptedCallback
"、"attributeChangedCallback
"、"formAssociatedCallback
"、"formDisabledCallback
"、"formResetCallback
" 和 "formStateRestoreCallback
"。相应的取值要么是 Web IDL Function
回调函数类型值,要么是 null。默认情况下,每个条目的值都为 null。attachInternals()
。attachShadow()
。为了 查找自定义元素定义,给定 document、namespace、localName 和 is,执行以下步骤。它们将返回 自定义元素定义 或 null
如果 namespace 不是 HTML 命名空间,则返回 null。
如果 document 的 浏览上下文 为 null,则返回 null。
令 registry 为 document 的 相关全局对象 的 CustomElementRegistry
对象。
如果 registry 中存在 自定义元素定义,其 名称 和 本地名称 都等于 localName,则返回该 自定义元素定义。
如果 registry 中存在 自定义元素定义,其 名称 等于 is 且 本地名称 等于 localName,则返回该 自定义元素定义。
返回 null。
CustomElementRegistry
接口所有当前引擎都支持。
每个 Window
对象都与 CustomElementRegistry
对象的唯一实例关联,该实例在创建 Window
对象时分配。
自定义元素注册表与 Window
对象关联,而不是与 Document
对象关联,因为每个 自定义元素构造函数 都继承自 HTMLElement
接口,并且每个 Window
对象只有一个 HTMLElement
接口。
所有当前引擎都支持。
Window
接口的 customElements
属性必须返回该 Window
对象的 CustomElementRegistry
对象。
[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 ;
};
每个 CustomElementRegistry
都有一个 自定义元素定义 集,最初为空。通常,本规范中的算法通过 名称、本地名称 或 构造函数 中的任何一个在注册表中查找元素。
每个 CustomElementRegistry
也都有一个 元素定义正在运行 标志,用于防止 元素定义 的重复调用。它最初未设置。
每个 CustomElementRegistry
也都有一个 已定义承诺映射,它将 有效的自定义元素名称 映射到承诺。它用于实现 whenDefined()
方法。
window.customElements.define(name, constructor)
所有当前引擎都支持。
window.customElements.define(name, constructor, { extends: baseLocalName })
NotSupportedError
" DOMException
。window.customElements.get(name)
所有当前引擎都支持。
window.customElements.getName(constructor)
window.customElements.whenDefined(name)
CustomElementRegistry/whenDefined
所有当前引擎都支持。
SyntaxError
" DOMException
拒绝。window.customElements.upgrade(root)
所有当前引擎都支持。
元素定义 是将一个 自定义元素定义 添加到 CustomElementRegistry
的过程。这是通过 define()
方法实现的。当调用时,define(name, constructor, options)
方法必须执行以下步骤
如果 IsConstructor(constructor) 为 false,则抛出 TypeError
。
如果 name 不是 有效的自定义元素名称,则抛出 "SyntaxError
" DOMException
。
如果此 CustomElementRegistry
包含具有 名称 name 的条目,则抛出 "NotSupportedError
" DOMException
。
如果此 CustomElementRegistry
包含具有 构造函数 constructor 的条目,则抛出 "NotSupportedError
" DOMException
。
令 localName 为 name。
令 extends 为 options 的 extends
成员的值,如果不存在此成员则为 null。
如果 extends 不为 null,则
如果 extends 是 有效的自定义元素名称,则抛出 "NotSupportedError
" DOMException
。
如果 extends 和 HTML 命名空间 的 元素接口 为 HTMLUnknownElement
(例如,如果 extends 在此规范中未指示元素定义),则抛出 "NotSupportedError
" DOMException
。
将 localName 设置为 extends。
如果此 CustomElementRegistry
的 元素定义正在运行 标志已设置,则抛出 "NotSupportedError
" DOMException
。
设置此 CustomElementRegistry
的 元素定义正在运行 标志。
令 formAssociated 为 false。
令 disableInternals 为 false。
令 disableShadow 为 false。
令 observedAttributes 为空的 sequence<DOMString>
。
运行以下子步骤,同时捕获任何异常
令 prototype 为 ? Get(constructor, "prototype").
令 lifecycleCallbacks 为一个映射,其键为 "connectedCallback
"、"disconnectedCallback
"、"adoptedCallback
" 和 "attributeChangedCallback
",每个键都属于一个其值为 null 的条目。
对于 lifecycleCallbacks 中的每个键 callbackName,按前一步中列出的顺序
如果 lifecycleCallbacks 中具有键 "attributeChangedCallback
" 的条目的值为非 null,则
令 observedAttributesIterable 为 ? Get(constructor, "observedAttributes").
如果 observedAttributesIterable 不为 undefined,则将 observedAttributes 设置为将 observedAttributesIterable 转换为 sequence<DOMString>
的结果。重新抛出转换过程中的任何异常。
令 disabledFeatures 为空的 sequence<DOMString>
。
令 disabledFeaturesIterable 为 ? Get(constructor, "disabledFeatures").
如果 disabledFeaturesIterable 不为 undefined,则将 disabledFeatures 设置为将 disabledFeaturesIterable 转换为 sequence<DOMString>
的结果。重新抛出转换过程中的任何异常。
如果 disabledFeatures 包含 "internals
",则将 disableInternals 设置为 true。
如果 disabledFeatures 包含 "shadow
",则将 disableShadow 设置为 true。
令 formAssociatedValue 为 ? Get( constructor, "formAssociated").
将 formAssociated 设置为将 formAssociatedValue 转换为 boolean
的结果。重新抛出转换过程中的任何异常。
如果 formAssociated 为 true,对于 "formAssociatedCallback
"、"formResetCallback
"、"formDisabledCallback
" 和 "formStateRestoreCallback
" 的每个 callbackName
然后,执行以下子步骤,无论上面的步骤是否抛出异常
取消设置此 CustomElementRegistry
的 元素定义正在运行 标志。
最后,如果第一组子步骤抛出异常,则重新抛出该异常(从而终止此算法)。否则,继续执行。
令 definition 为一个新的 自定义元素定义,具有 名称 name、本地名称 localName、构造函数 constructor、观察属性 observedAttributes、生命周期回调 lifecycleCallbacks、与表单关联 formAssociated、禁用内部 disableInternals 和 禁用阴影 disableShadow。
将 definition 添加到此 CustomElementRegistry
。
令 document 为此 CustomElementRegistry
的 相关全局对象 的 关联 Document
。
令 升级候选者 为所有在 包含阴影的树的顺序 中,是 document 的 包含阴影的后代,其命名空间为 HTML 命名空间 且其本地名称为 localName 的元素。此外,如果 extends 不为 null,则仅包括 is
值 等于 name 的元素。
对于 升级候选者 中的每个元素 element,将一个自定义元素升级反应排队,其中给定 element 和 definition。
如果此 CustomElementRegistry
的 已定义承诺映射 包含具有键 name 的条目
令 promise 为该条目的值。
用 constructor 解决 promise。
从此 CustomElementRegistry
的 已定义承诺映射 中删除具有键 name 的条目。
当调用时,get(name)
方法必须执行以下步骤
如果此 CustomElementRegistry
包含具有 名称 name 的条目,则返回该条目的 构造函数。
否则,返回 undefined。
getName(constructor)
方法的步骤如下
如果此 CustomElementRegistry
包含一个条目,其 构造函数 为 constructor,则返回该条目的 名称。
返回 null。
当调用时,whenDefined(name)
方法必须执行以下步骤
如果 name 不是一个 有效的自定义元素名称,则返回 一个被以下内容拒绝的 Promise:一个 "SyntaxError
" DOMException
。
如果此 CustomElementRegistry
包含一个条目,其 名称 为 name,则返回 一个被以下内容决议的 Promise:该条目的 构造函数。
令 map 为此 CustomElementRegistry
的 when-defined Promise 映射。
如果 map 不包含一个键为 name 的条目,则在 map 中创建一个键为 name 的条目,其值为一个新的 Promise。
令 promise 为 map 中键为 name 的条目的值。
返回 promise。
方法可用于避免在所有相关的 自定义元素 定义 之前执行操作。在此示例中,我们将它与 whenDefined()
伪类结合起来,以隐藏动态加载的文章内容,直到我们确定它使用的所有 自治自定义元素 都已定义。: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 ;
});
当调用时,upgrade(root)
方法必须执行以下步骤
方法允许根据需要升级元素。通常元素会在它们变得 连接 时自动升级,但如果您需要在准备连接元素之前升级,可以使用此方法。upgrade()
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!
要 升级一个元素,将一个 自定义元素定义 definition 和一个元素 element 作为输入,执行以下步骤
如果 element 的 自定义元素状态 不是 "undefined
" 或 "uncustomized
",则返回。
由于此算法的递归调用,这种情况可能发生在以下示例中
<!DOCTYPE html>
< x-foo id = "a" ></ x-foo >
< x-foo id = "b" ></ x-foo >
< script >
// Defining enqueues upgrade reactions for both "a" and "b"
customElements. define( "x-foo" , class extends HTMLElement {
constructor() {
super ();
const b = document. querySelector( "#b" );
b. remove();
// While this constructor is running for "a", "b" is still
// undefined, and so inserting it into the document will enqueue a
// second upgrade reaction for "b" in addition to the one enqueued
// by defining x-foo.
document. body. appendChild( b);
}
})
</ script >
因此,当 升级一个元素 使用 "b
" 第二次调用时,此步骤将尽早退出算法。
将 element 的 自定义元素定义 设置为 definition。
将 element 的 自定义元素状态 设置为 "failed
"。
它将在 升级成功后 设置为 "custom
"。现在,我们将其设置为 "failed
",以便任何递归调用都会命中 上述的早期退出步骤。
对于 element 的 属性列表 中的每个 attribute,按顺序 将一个自定义元素回调反应入队,其 element,回调名称为 "attributeChangedCallback
",并且参数列表包含 attribute 的本地名称、null、attribute 的值以及 attribute 的命名空间。
如果 element 已 连接,则 将一个自定义元素回调反应入队,其 element,回调名称为 "connectedCallback
",并且参数列表为空。
将 element 添加到 definition 的 构造栈 的末尾。
令 C 为 definition 的 构造函数。
运行以下子步骤,同时捕获任何异常
如果 definition 的 禁用阴影 为 true 并且 element 的 影子根 不为 null,则抛出一个 "NotSupportedError
" DOMException
。
这是必需的,因为
在调用时不使用 查找自定义元素定义,而 attachShadow()
会使用。attachInternals()
将 element 的 自定义元素状态 设置为 "precustomized
"。
令 constructResult 为使用无参数 构造 C 的结果。
如果 C 不符合规范地 使用带有
扩展属性的 API,那么在此算法开始时入队的反应将在此步骤期间执行,在 C 完成并控制权返回到此算法之前。否则,它们将在 C 和升级过程的其余部分完成后执行。[CEReactions]
如果 SameValue(constructResult, element) 为 false,则抛出一个
。TypeError
如果 C 在调用 super()
之前构造了同一个自定义元素的另一个实例,或者如果 C 使用 JavaScript 的 return
覆盖功能从构造函数中返回一个任意的
对象,则可能会发生这种情况。HTMLElement
然后,执行以下子步骤,无论上面的步骤是否抛出异常
从 definition 的 构造栈 的末尾移除最后一个条目。
假设 C 调用 super()
(如果它 符合规范,则会这样做),并且调用成功,这将是我们在此算法开始时压入的 element 所替换的 已构造 标记。(HTML 元素构造函数 执行此替换。)
如果 C 没有调用 super()
(即它 不符合规范),或者 HTML 元素构造函数 中的任何步骤都抛出异常,则此条目仍然为 element。
最后,如果上述步骤抛出异常,则
如果上述步骤抛出异常,则 element 的 自定义元素状态 将保持 "failed
" 或 "precustomized
"。
如果 element 是一个 与表单关联的自定义元素,则
重置 element 的表单所有者。如果 element 与一个
元素关联,则 将一个自定义元素回调反应入队,其 element,回调名称为 "form
formAssociatedCallback
",并且参数为 « 关联的
»。form
如果 element 已禁用,则 将一个自定义元素回调反应入队,其 element,回调名称为 "formDisabledCallback
",并且参数为 « true »。
将 element 的 自定义元素状态 设置为 "custom
"。
要 尝试升级一个元素,将元素 element 作为输入,执行以下步骤
令 definition 为 查找自定义元素定义 的结果,该定义使用 element 的 节点文档、element 的命名空间、element 的本地名称以及 element 的 is
值。
如果 definition 不为 null,则 将一个自定义元素升级反应入队,其 element 和 definition。
一个 自定义元素 具有通过运行作者代码来响应某些事件的能力
当它 变得连接 时,其 connectedCallback
将被调用,无参数。
当它断开连接时,将调用其disconnectedCallback
,不带任何参数。
当它被采用到一个新的文档中时,将调用其adoptedCallback
,并将旧文档和新文档作为参数传递。
当它的任何属性被改变、添加、删除或替换时,将调用其attributeChangedCallback
,并将属性的本地名称、旧值、新值和命名空间作为参数传递。(当属性分别被添加或删除时,属性的旧值或新值被认为是 null。)
当用户代理重置一个与表单关联的自定义元素的表单所有者,并且这样做改变了表单所有者时,将调用其formAssociatedCallback
,并将新的表单所有者(如果沒有所有者则为 null)作为参数传递。
当一个与表单关联的自定义元素的表单所有者被重置时,将调用其formResetCallback
。
当一个与表单关联的自定义元素的禁用状态改变时,将调用其formDisabledCallback
,并将新的状态作为参数传递。
当用户代理代表用户更新一个与表单关联的自定义元素的值,或者作为导航的一部分时,将调用其formStateRestoreCallback
,并将新的状态和一个字符串作为参数传递,该字符串表示原因,"autocomplete
"或"restore
"。
我们把这些反应统称为自定义元素反应。
调用自定义元素反应的方式需要特别小心,以避免在执行复杂操作的过程中运行作者代码。实际上,它们被延迟到 "在返回用户脚本之前"。这意味着,对于大多数情况,它们看起来是同步执行的,但在复杂复合操作(如克隆或范围操作)的情况下,它们将被延迟到所有相关的用户代理处理步骤完成后,然后作为一个批处理一起运行。
此外,这些反应的精确顺序通过一个稍微复杂的队列堆栈系统进行管理,下面将对此进行描述。这个系统背后的意图是保证自定义元素反应总是按照它们触发操作的顺序调用,至少在单个自定义元素的本地上下文中是如此。(因为自定义元素反应代码可以执行它自己的修改,所以不可能跨多个元素给出全局顺序保证。)
每个同源窗口代理都有一个自定义元素反应堆栈,该堆栈最初为空。一个同源窗口代理的当前元素队列是其自定义元素反应堆栈顶部的元素队列。堆栈中的每一项都是一个元素队列,它最初也是空的。一个元素队列中的每一项都是一个元素。(这些元素不一定是自定义的,因为这个队列也用于升级。)
每个自定义元素反应堆栈都有一个相关的备份元素队列,它最初是一个空的元素队列。在影响 DOM 但不通过带有[CEReactions]
装饰的 API 或通过解析器的创建元素算法进行的操作期间,元素会被推送到备份元素队列中。一个例子是用户发起的编辑操作,它修改了可编辑元素的后代或属性。为了防止在处理备份元素队列时出现重入,每个自定义元素反应堆栈还有一个处理备份元素队列标志,最初未设置。
所有元素都有一个相关的自定义元素反应队列,最初为空。自定义元素反应队列中的每一项都是以下两种类型之一
所有这些都总结在下面的示意图中
要将元素排队到相应的元素队列,给定一个元素element,执行以下步骤
如果reactionsStack为空,则
要排队一个自定义元素回调反应,给定一个自定义元素element、一个回调名称callbackName和一个参数列表args,执行以下步骤
让definition成为element的自定义元素定义。
让callback成为definition的生命周期回调中键为callbackName的条目的值。
如果callback为 null,则返回。
如果callbackName为 "attributeChangedCallback
",则
让attributeName成为args的第一个元素。
如果definition的观察属性不包含attributeName,则返回。
给定element,将元素排队到相应的元素队列。
要排队一个自定义元素升级反应,给定一个元素element和一个自定义元素定义definition,执行以下步骤
给定element,将元素排队到相应的元素队列。
要在一个元素队列queue中调用自定义元素反应,执行以下步骤
为了确保 自定义元素反应 被适当地触发,我们引入了 [CEReactions]
IDL 扩展属性。它表明相关算法需要补充额外的步骤,以适当地跟踪和调用 自定义元素反应。
[CEReactions]
扩展属性不能接受任何参数,并且不能出现在操作、属性、setter 或 deleter 以外的任何地方。此外,它不能出现在只读属性上。
使用 [CEReactions]
扩展属性注释的操作、属性、setter 或 deleter 必须运行以下步骤,而不是其描述中指定的步骤。
运行此构造的最初指定的步骤,捕获任何异常。如果步骤返回值,则让 value 为返回值。如果它们抛出异常,则让 exception 为抛出的异常。
调用 queue 中的自定义元素反应。
如果原始步骤抛出异常 exception,则重新抛出 exception。
如果原始步骤返回一个值 value,则返回 value。
此扩展属性背后的意图有些微妙。实现其目标的一种方法是说,平台上的每个操作、属性、setter 和 deleter 都必须插入这些步骤,并允许实现者优化掉不必要的案例(在其中不可能出现导致 自定义元素反应 发生的 DOM 突变)。
然而,在实践中,这种不精确性可能导致 自定义元素反应 的非互操作实现,因为某些实现可能会忘记在某些情况下调用这些步骤。相反,我们选择明确地注释所有相关的 IDL 构造,作为确保互操作行为和帮助实现轻松找出需要这些步骤的所有情况的一种方式。
用户代理引入的任何非标准 API,这些 API 可能以可能导致 将自定义元素回调反应入队 或 将自定义元素升级反应入队 的方式修改 DOM(例如,通过修改任何属性或子元素),也必须使用 [CEReactions]
属性进行装饰。
截至撰写本文时,已知以下非标准或尚未标准化的 API 属于此类别。
HTMLInputElement
的 webkitdirectory
和 incremental
IDL 属性。
HTMLLinkElement
的 scope
IDL 属性。
某些功能旨在让自定义元素作者可以使用,而不是让自定义元素使用者可以使用。这些功能由 element.attachInternals()
方法提供,该方法返回一个 ElementInternals
实例。 ElementInternals
的属性和方法允许控制用户代理为所有元素提供的内部功能。
element.attachInternals()
返回一个针对 自定义元素 element 的 ElementInternals
对象。如果 element 不是 自定义元素,或者如果 "internals
" 功能在元素定义中被禁用,或者如果它在同一个元素上被调用两次,则抛出异常。
每个 HTMLElement
都有一个 已附加的内部(null 或一个 ElementInternals
对象),最初为 null。
所有当前引擎都支持。
attachInternals()
方法的步骤如下:
如果 this 的 is
值 不是 null,则抛出一个 "NotSupportedError
" DOMException
。
让 definition 为根据 this 的 节点文档、其命名空间、其本地名称以及 null 作为 is
值 查找自定义元素定义 的结果。
如果 definition 为 null,则抛出一个 "NotSupportedError
" DOMException
。
如果 definition 的 禁用内部 为 true,则抛出一个 "NotSupportedError
" DOMException
。
如果 this 的 已附加的内部 不是 null,则抛出一个 "NotSupportedError
" DOMException
。
如果 this 的 自定义元素状态 不是 "precustomized
" 或 "custom
",则抛出一个 "NotSupportedError
" DOMException
。
将 this 的 已附加的内部 设置为一个新的 ElementInternals
实例,其 目标元素 为 this。
ElementInternals
接口所有当前引擎都支持。
ElementInternals
接口的 IDL 如下所示,各种操作和属性在以下部分定义
[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 ;
};
每个 ElementInternals
都有一个 目标元素,它是一个 自定义元素。
internals.shadowRoot
返回 internals 的 目标元素 的 ShadowRoot
,如果 目标元素 是一个 阴影宿主,否则返回 null。
所有当前引擎都支持。
shadowRoot
getter 的步骤如下:
如果 target 不是一个 阴影宿主,则返回 null。
让 shadow 为 target 的 阴影根。
如果 shadow 的 对元素内部可用 为 false,则返回 null。
返回 shadow。
internals.setFormValue(value)
将 internals 的 目标元素 的 状态 和 提交值 同时设置为 value。
如果 value 为 null,则该元素将不参与表单提交。
internals.setFormValue(value, state)
将 internals 的 目标元素 的 提交值 设置为 value,并将它的 状态 设置为 state。
如果 value 为 null,则该元素将不参与表单提交。
internals.form
internals.setValidity(flags, message [, anchor ])
将 internals 的 目标元素 标记为存在 flags 参数指示的约束问题,并将元素的验证消息设置为 message。如果指定了 anchor,用户代理可能会在 表单所有者 以交互方式验证或调用 reportValidity()
时,使用它来指示 internals 的 目标元素 约束方面的问题。
internals.setValidity({})
internals.willValidate
如果 internals 的 目标元素 在提交表单时将被验证,则返回 true;否则返回 false。
internals.validity
返回 internals 的 目标元素 的 ValidityState
对象。
internals.validationMessage
返回如果 internals 的 目标元素 要进行有效性检查,将向用户显示的错误消息。
valid = internals.checkValidity()
如果 internals 的 目标元素 没有有效性问题,则返回 true;否则返回 false。在后一种情况下,在元素上触发 invalid
事件。
valid = internals.reportValidity()
如果 internals 的 目标元素 没有有效性问题,则返回 true;否则,返回 false,在元素上触发 invalid
事件,并且(如果事件没有被取消)将问题报告给用户。
internals.labels
每个 与表单关联的自定义元素 都有一个 提交值。它用于在表单提交时提供一个或多个 条目。提交值 的初始值为 null,提交值 可以为 null、字符串、File
或 list of 条目。
每个 与表单关联的自定义元素 都有 状态。它包含用户代理可以用来恢复用户输入的信息。状态 的初始值为 null,状态 可以为 null、字符串、File
或 list of 条目。
自定义元素作者使用 setFormValue()
方法来设置元素的 提交值 和 状态,从而将这些信息传达给用户代理。
当用户代理认为恢复 与表单关联的自定义元素 的 状态 是一个好主意时(例如,导航后 或重新启动用户代理),他们可能会将自定义元素回调反应排队,其中包含该元素,回调名称“formStateRestoreCallback
”,包含要恢复的状态的 arguments 列表,以及“restore
”。
如果用户代理具有表单填充辅助功能,那么当该功能被调用时,它可能会将自定义元素回调反应排队,其中包含一个 与表单关联的自定义元素,回调名称“formStateRestoreCallback
”,包含由状态值历史记录和一些启发式方法确定的状态值的 arguments 列表,以及“autocomplete
”。
通常,状态 是用户指定的信息,提交值 是规范化或清理后的值,适合提交给服务器。以下示例具体说明了这一点。
假设我们有一个 与表单关联的自定义元素,它要求用户指定一个日期。用户指定了 "3/15/2019",但该控件希望将 "2019-03-15"
提交给服务器。"3/15/2019" 将是元素的 状态,"2019-03-15"
将是 提交值。
假设您开发了一个自定义元素,它模拟了现有 checkbox input
类型的行为。它的 提交值 将是其 value
内容属性的值,或者字符串 "on"
。它的 状态 将是 "checked"
、"unchecked"
、"checked/indeterminate"
或 "unchecked/indeterminate"
之一。
所有当前引擎都支持。
setFormValue(value, state)
方法的步骤是
如果 element 不是 与表单关联的自定义元素,则抛出一个 "NotSupportedError
" DOMException
。
如果 value 不是 FormData
对象,则将 目标元素 的 提交值 设置为 value,否则将 目标元素 的 提交值 设置为 value 的 克隆 的 条目列表。
否则,如果 state 是 FormData
对象,则将 element 的 状态 设置为 state 的 克隆 的 条目列表。
否则,将 element 的 状态 设置为 state。
每个 与表单关联的自定义元素 都有名为 valueMissing
、typeMismatch
、patternMismatch
、tooLong
、tooShort
、rangeUnderflow
、rangeOverflow
、stepMismatch
和 customError
的有效性标志。它们最初为 false。
每个 与表单关联的自定义元素 都有一个 验证消息 字符串。它最初为空字符串。
每个 与表单关联的自定义元素 都有一个 验证锚点 元素。它最初为 null。
所有当前引擎都支持。
setValidity(flags, message, anchor)
方法的步骤是
如果 element 不是 与表单关联的自定义元素,则抛出一个 "NotSupportedError
" DOMException
。
如果 flags 包含一个或多个 true 值,并且 message 未给出或为空字符串,则抛出一个 TypeError
。
对于 flags 中的每个条目 flag → value,将名称为 flag 的 element 的有效性标志设置为 value。
如果 message 未给出或 element 的所有有效性标志都为 false,则将 element 的 验证消息 设置为空字符串,否则将其设置为 message。
如果 element 的 customError
有效性标志为 true,则将 element 的 自定义有效性错误消息 设置为 element 的 验证消息。否则,将 element 的 自定义有效性错误消息 设置为空字符串。
如果 anchor 未给出,则将 element 的 验证锚点 设置为 null。否则,如果 anchor 不是 element 的 影子包含后代,则抛出一个 "NotFoundError
" DOMException
。否则,将 element 的 验证锚点 设置为 anchor。
ElementInternals/validationMessage
所有当前引擎都支持。
以下步骤描述了 validationMessage
获取器。
如果 element 不是 与表单关联的自定义元素,则抛出一个 "NotSupportedError
" DOMException
。
返回 element 的 验证消息。
对于 与表单关联的自定义元素,给定元素 element 和 条目列表 entry list,条目构造算法 包含以下步骤:
如果 element 的 提交值 是一个 列表,其中包含 条目,则将 element 的 提交值 中的每个项目 追加 到 entry list 中,并返回。
在这种情况下,用户代理不引用 name
内容属性值。 与表单关联的自定义元素 的实现负责决定 条目 的名称。它们可以是 name
内容属性值,它们可以是基于 name
内容属性值的字符串,或者它们可以与 name
内容属性无关。
如果元素的 提交值 不是空值,则 创建一个条目,其 name
属性值为 提交值,并 追加 到 entry list 中。
internals.role [ = value ]
设置或获取 internals 的 目标元素 的默认 ARIA 角色,除非页面作者使用 role
属性覆盖它,否则将使用此角色。
internals.aria* [ = value ]
设置或获取 internals 的 目标元素 的各种默认 ARIA 状态或属性值,除非页面作者使用 aria-*
属性覆盖它们,否则将使用这些值。
每个 自定义元素 都有一个 内部内容属性映射,这是一个 映射,最初为空。有关这如何影响平台可访问性 API 的信息,请参阅 与 ARIA 和平台可访问性 API 相关的要求 部分。
internals.states.add(value)
将字符串 value 添加到元素的 状态集 中,以便将其作为伪类公开。
internals.states.has(value)
如果 value 在元素的 状态集 中,则返回 true,否则返回 false。
internals.states.delete(value)
如果元素的 状态集 包含 value,则将其删除并返回 true。否则,返回 false。
internals.states.clear()
从元素的 状态集 中删除所有值。
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())
遍历元素的 状态集 中的所有值。
internals.states.forEach(callback)
通过对元素的 状态集 中的每个值调用 callback 一次,来遍历所有值。
internals.states.size
返回元素的 状态集 中的值的数量。
每个 自定义元素 都有一个 状态集,它是一个 CustomStateSet
,最初为空。
[Exposed =Window ]
interface CustomStateSet {
setlike <DOMString >;
};
以下步骤描述了 states
获取器:返回 this 的 目标元素 的 状态集。
可以通过字符串值的存在/不存在来公开 状态集 中表示的布尔状态。如果作者想要公开一个可以有三个值的 state,则可以将其转换为三个互斥的布尔状态。例如,一个名为 readyState
的 state,具有 "loading"
、"interactive"
和 "complete"
值,可以映射到三个互斥的布尔状态:"loading"
、"interactive"
和 "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" );