網頁功能:
與表單相關聯的自訂元素
請勿改用隱藏的 <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。請分別使用InputEvent和Event建構。
備用策略
請先使用 static formAssociated 支援功能偵測功能,再依賴該功能:'attachInternals' in HTMLElement.prototype && 'setFormValue' in ElementInternals.prototype。如果無法使用,請改用與控制項值同步的原生 <input>,並在視覺上隱藏,這樣表單仍可提交。這是唯一適合使用隱藏輸入模式的情況。