عناصر سفارشی مرتبط با فرم

برای نمایش مقدار کنترل سفارشی در فرم، به <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> بومی پنهان‌شده ازنظر بصری که با مقدار کنترل همگام‌سازی شده است برگردید تا فرم همچنان ارسال شود — تنها موردی که الگوی ورودی پنهان توجیه می‌شود.