ウェブ機能:
フォームに関連付けられたカスタム要素
カスタム コントロールの値をフォームに表示するために、非表示の <input> を代替使用しないでください。フォームに関連付けられたカスタム要素(FACE)を使用する: formAssociated と ElementInternals を使用すると、要素は独自の値の送信、検証への参加、フォームの状態のネイティブな反映を行うことができます。
class RatingInput extends HTMLElement {
// MANDATORY: opt in. Without this, attachInternals() form APIs are unavailable.
static formAssociated = true;
#internals;
#value = '';
constructor() {
super();
this.#internals = this.attachInternals();
this.attachShadow({ mode: 'open', delegatesFocus: true });
}
connectedCallback() {
// Validate initial state once attributes (such as `required`) are present.
this.#validate();
}
get value() { return this.#value; }
set value(v) {
this.#value = v;
// Push the value into the owning <form> for submission.
this.#internals.setFormValue(v);
this.#validate();
}
#validate() {
if (this.hasAttribute('required') && !this.#value) {
// Mark invalid and supply the native validation message + anchor.
this.#internals.setValidity(
{ valueMissing: true },
'Please choose a rating.',
this.shadowRoot.querySelector('[tabindex]'),
);
} else {
this.#internals.setValidity({}); // clear
}
}
// Optional FACE lifecycle hooks:
formResetCallback() { this.value = ''; }
formDisabledCallback(disabled) {
// Disable internal controls so they leave the tab order and ignore clicks.
// Do NOT toggle `disabled` on `this`, or a `<fieldset disabled>` toggle will
// leave the host permanently stuck with its own `disabled` attribute.
for (const el of this.shadowRoot.querySelectorAll('button')) {
el.disabled = disabled;
}
}
formStateRestoreCallback(state) { this.value = state; }
}
customElements.define('rating-input', RatingInput);
要点:
static formAssociated = trueは必須です。フォーム関連のElementInternalsメソッドとformXxxCallbackライフサイクルのロックを解除します。setFormValue(value)は、非表示の入力が不要な送信です。複数値コントロールの場合はFormDataを渡します。setValidity(flags, message, anchor)はネイティブの制約検証と統合されています。無効な場合はフォームが送信されず、:invalid/:validが適用され、レポートでアンカー要素がフォーカスされます。setValidity({})を呼び出してクリアします。インタラクション対応の:user-valid/:user-invalid疑似クラスは、ブラウザ間でフォーム関連要素に一貫して接続されているわけではありません。ネイティブ コントロールと同じようにユーザー インタラクションを追跡すると想定するのではなく、サポートを確認してから使用してください。formResetCallback、formDisabledCallback、formStateRestoreCallbackを実装して、リセット、fieldset[disabled]、戻る / 進むの自動入力復元時に、要素がネイティブ コントロールのように動作するようにします。formDisabledCallback(disabled)では、ホストにdisabledを設定するのではなく、内部コントロールを無効にします(または、タブオーダーから削除して入力を無視します)。コールバック内でホスト属性を設定すると、祖先の<fieldset>が再度有効になったときに再度有効にできなくなります。また、ホストはすでに:disabledに自動的に一致しています。- ディスパッチされるイベントについてはプラットフォームの規則に従い、カスタム イベントよりも組み込みの
inputイベントとchangeイベントを優先します。inputは、ユーザーの操作(入力、スライダーのドラッグ)の直接的な結果として値が変更されたときに起動します。changeは、ユーザーがコミット(スライダーのリリース、フィールドのぼかし)したときに、変更のストリームごとに 1 回起動します。それぞれInputEventとEventで作成します。
代替戦略
static formAssociated のサポートに依存する前に、'attachInternals' in HTMLElement.prototype && 'setFormValue' in ElementInternals.prototype で機能検出を行います。利用できない場合は、コントロールの値に同期された、視覚的に非表示のネイティブ <input> にフォールバックして、フォームが送信されるようにします。これは、非表示入力パターンが正当化される唯一のケースです。