ElementtInternals
Web Components
ElementInternals とは #
ElementInternalsとは、カスタム要素が標準のHTML要素と同じようにフォームを構成する要素として機能できるようにするためのものです。
formAssociatedというstaticプロパティをtrueで設定することでそれを囲う<form>と関連づけることができ、フォーム送信時にフォーム検証ができるようになります。他にもARIA属性をつけられるようになったり、カスタムstateを設定してstateに応じたスタイリングができるようになったり、後日紹介するElementInternals.typeが検討されていたりします。
https://webkit.org/blog/13711/elementinternals-and-form-associated-custom-elements/ https://web.dev/articles/more-capable-form-controls?hl=ja#defining_a_form-associated_custom_element
これらの機能をもう少し詳しく説明していきます。
できること #
バリデーション #
ElementInternalsに生えているメソッドであるsetFormValue()で値を設定したり、setValidity()、checkValidity()及びreportValidity()でバリデーションを行うことができます。
onUpdateValue() {
if (!this.matches(':disabled') && this.hasAttribute('required') &&
this.value_ < 0) {
this.internals_.setValidity({customError: true}, 'Value cannot be negative.');
} else {
this.internals_.setValidity({});
}
this.internals.setFormValue(this.value_);
}
state #
カスタム要素単位で、stateをKVストアの形で持つことができるCustomStateSetを扱うことができます。
set checked(flag) {
if (flag) {
this._internals.states.add('--checked');
} else {
this._internals.states.delete('--checked');
}
}
これをCSSの:state(<identifier>)のidentifierとして用いることで、カスタム要素を状態に応じてスタイリングすることができます。
labeled-checkbox {
border: dashed red;
}
labeled-checkbox:state(--checked) {
border: solid;
}
ARIA #
今までの問題点:
- setAttributeはアップグレードされたときに実行されるので、カスタム要素にユーザーがHTMLで指定した属性を上書きしてしまう
- setAttributeした属性が後から上書きされてさらに消されたとき、デフォルト値が消えてしまう
子要素がどうとかではなくて、カスタム要素自体を1つのHTML要素として扱うときの話 →カスタム組み込み要素も同じ感じのイメージかも
<script>
class CustomTabList extends HTMLElement {
constructor() {
super();
this._internals = this.attachInternals();
this._internals.role = "tablist";
this._internals.ariaOrientation = "horizontal";
}
}
customElements.define("custom-tablist", CustomTabList);
class CustomTab extends HTMLElement {
constructor() {
super();
this._internals = this.attachInternals();
this._internals.role = "tab";
this._internals.ariaSelected = false;
}
}
customElements.define("custom-tab", CustomTab);
</script>
<custom-tablist aria-orientation="vertical" class="vertical-tablist">
<custom-tab aria-selected="true">Tab 1</custom-tab>
<custom-tab>Tab 2</custom-tab>
<custom-tab>Tab 3</custom-tab>
</custom-tablist>
宣言的・シリアル化可能な方法 #
今見てきた方法は全てJSを用いた命令的にElementInternalsを設定する方法でした。 しかし、JSが読み込まれていないタイミング・環境でも利用可能にしたかったり、SSRやSEOの文脈でもメリットがあることから、宣言的・シリアル化可能な方法が求められます。 これはShadow DOMの宣言でも同じことが求められてDeclarative Shadow DOMが誕生しました。カスタム要素の定義でも同様で、現在DeclarativeなCustom Elementsの定義方法が検討されています。