Elemen Kustom Terkait Formulir

Jangan kembali ke <input> tersembunyi untuk menampilkan nilai kontrol kustom ke formulir. Gunakan Elemen Kustom Terkait Formulir (FACE): dengan formAssociated dan ElementInternals, elemen akan mengirimkan nilainya sendiri, berpartisipasi dalam validasi, dan mencerminkan status formulir secara native.

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);

Poin-poin utama:

  • static formAssociated = true bersifat WAJIB; metode ini membuka metode ElementInternals terkait formulir dan siklus proses formXxxCallback.
  • setFormValue(value) adalah elemen yang mengirimkan, tanpa memerlukan input tersembunyi. Teruskan FormData untuk kontrol multi-nilai.
  • setValidity(flags, message, anchor) terintegrasi dengan validasi batasan native: formulir tidak akan dikirim jika tidak valid, :invalid/:valid berlaku, dan elemen anchor menerima fokus pada laporan. Panggil setValidity({}) untuk menghapus. Class pseudo :user-valid/:user-invalid yang mendukung interaksi tidak terhubung secara konsisten untuk elemen terkait formulir di seluruh browser — verifikasi dukungan sebelum mengandalkannya, bukan berasumsi bahwa class tersebut melacak interaksi pengguna seperti yang dilakukan untuk kontrol native.
  • Terapkan formResetCallback, formDisabledCallback, dan formStateRestoreCallback agar elemen berperilaku seperti kontrol native saat direset, fieldset[disabled], dan pemulihan isi otomatis kembali/maju. Di formDisabledCallback(disabled), nonaktifkan kontrol internal (atau hapus dari urutan tab dan abaikan input) daripada menetapkan disabled pada host — menetapkan atribut host di dalam callback akan mencegah pengaktifan kembali saat ancestor <fieldset> diaktifkan kembali, dan host sudah cocok dengan :disabled secara otomatis.
  • Ikuti konvensi platform untuk peristiwa yang dikirim dan pilih peristiwa input dan change bawaan daripada peristiwa kustom. input diaktifkan saat nilai berubah sebagai akibat langsung dari tindakan pengguna (mengetik, menarik penggeser); change diaktifkan sekali per aliran perubahan, saat pengguna melakukan tindakan (melepaskan penggeser, memburamkan kolom). Buat dengan InputEvent dan Event.

Strategi penggantian

Deteksi fitur dengan dukungan static formAssociated sebelum mengandalkannya: 'attachInternals' in HTMLElement.prototype && 'setFormValue' in ElementInternals.prototype. Jika tidak tersedia, gunakan <input> native yang disembunyikan secara visual dan disinkronkan ke nilai kontrol sehingga formulir tetap dikirimkan — satu kasus yang membenarkan pola input tersembunyi.