لا تلجأ إلى استخدام عنصر <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> أصلي مخفي بصريًا ومتزامن مع قيمة عنصر التحكّم لكي يستمر إرسال النموذج، وهي الحالة الوحيدة التي يكون فيها نمط الإدخال المخفي مبررًا.