Start · Sprachen · JavaScript · Referenz · ElementInternals.setValidity()

ElementInternals.setValidity()

Methode

Setzt den Gültigkeitszustand eines formularassoziierten Custom Elements samt Fehlermeldung und optionalem Fokus-Anker für die Browser-Validierung.

seit JavaScript Baseline 2023: Chrome 77+, Firefox 98+, Safari 16. Kategorie: webapi

Signatur

ElementInternals.prototype.setValidity(flags, message, anchor)

Beschreibung

setValidity() ist das Gegenstück zu setCustomValidity() nativer Eingabefelder, aber für formularassoziierte Custom Elements, also Klassen mit static formAssociated = true, die über this.attachInternals() ein ElementInternals-Objekt erhalten. Damit nimmt ein eigenes Steuerelement vollständig an der Constraint Validation API teil: form.checkValidity(), reportValidity(), die CSS-Pseudoklassen :invalid/:valid und das Blockieren des Absendens funktionieren wie bei einem <input>.

Das erste Argument ist ein Objekt mit Flags, die denen von ValidityState entsprechen (valueMissing, typeMismatch, patternMismatch, tooLong, tooShort, rangeUnderflow, rangeOverflow, stepMismatch, badInput, customError). Ist mindestens ein Flag true, gilt das Element als ungültig, und dann muss eine nicht leere Meldung übergeben werden. Ein leeres Objekt {} setzt das Element wieder auf gültig.

Mit dem optionalen anchor gibst du ein Element aus dem Shadow DOM an, auf das der Browser bei reportValidity() den Fokus setzt und an dem er die Fehler-Blase anzeigt, zum Beispiel das interne <input>. Normalerweise rufst du setValidity() bei jeder Wertänderung neu auf, direkt nach setFormValue().

Parameter

Name Typ Default Beschreibung
$flags Object {} Objekt mit booleschen Gültigkeits-Flags (valueMissing, typeMismatch, patternMismatch, tooLong, tooShort, rangeUnderflow, rangeOverflow, stepMismatch, badInput, customError). Ein leeres Objekt oder ein Aufruf ohne Argumente markiert das Element als gültig.
$message string Fehlermeldung, die in validationMessage landet und in der Fehler-Blase des Browsers erscheint. Pflicht, sobald ein Flag true ist.
$anchor HTMLElement Element im Shadow DOM des Custom Elements, das bei reportValidity() den Fokus und die Fehleranzeige bekommt. Es muss ein Nachfahre im Shadow Tree sein.

Beispiele

Pflichtfeld-Validierung in einem eigenen Eingabe-Element

class RatingInput extends HTMLElement {
  static formAssociated = true;

  #internals = this.attachInternals();
  #value = 0;

  connectedCallback() {
    this.#update();
  }

  set value(v) {
    this.#value = Number(v);
    this.#update();
  }

  get value() {
    return this.#value;
  }

  #update() {
    this.#internals.setFormValue(String(this.#value));
    if (this.hasAttribute('required') && this.#value === 0) {
      this.#internals.setValidity(
        { valueMissing: true },
        'Bitte eine Bewertung von 1 bis 5 Sternen abgeben.'
      );
    } else {
      this.#internals.setValidity({}); // gültig
    }
  }

  checkValidity() { return this.#internals.checkValidity(); }
  get validationMessage() { return this.#internals.validationMessage; }
}

customElements.define('rating-input', RatingInput);

const el = document.createElement('rating-input');
el.setAttribute('required', '');
document.body.append(el);
console.log(el.checkValidity(), el.validationMessage);
el.value = 4;
console.log(el.checkValidity());
false "Bitte eine Bewertung von 1 bis 5 Sternen abgeben." true

Validierung am internen Input verankern (anchor)

class ZipField extends HTMLElement {
  static formAssociated = true;

  constructor() {
    super();
    this.internals = this.attachInternals();
    const root = this.attachShadow({ mode: 'open' });
    root.innerHTML = '<input part="input" inputmode="numeric">';
    this.input = root.querySelector('input');
    this.input.addEventListener('input', () => this.#validate());
  }

  connectedCallback() {
    this.#validate();
  }

  #validate() {
    const val = this.input.value;
    this.internals.setFormValue(val);
    if (!/^\d{5}$/.test(val)) {
      this.internals.setValidity(
        { patternMismatch: true },
        'Die Postleitzahl muss aus genau 5 Ziffern bestehen.',
        this.input // Fokus und Fehler-Blase am inneren Input
      );
    } else {
      this.internals.setValidity({});
    }
  }
}

customElements.define('zip-field', ZipField);

// <form><zip-field name="plz"></zip-field><button>Senden</button></form>
// Beim Absenden blockiert der Browser und fokussiert das innere <input>.

Asynchrone Serverprüfung mit customError

const checkUsername = async (internals, input) => {
  const name = input.value.trim();
  internals.setFormValue(name);
  const res = await fetch(`/api/users/exists?name=${encodeURIComponent(name)}`);
  const { exists } = await res.json();

  if (exists) {
    internals.setValidity(
      { customError: true },
      `Der Benutzername „${name}“ ist bereits vergeben.`,
      input
    );
  } else {
    internals.setValidity({});
  }
  console.log(internals.validity.customError, internals.validity.valid);
};
true false (falls der Name vergeben ist)

// Wichtig · Fallstricke

  • Nur für formularassoziierte Elemente: Ohne static formAssociated = true wirft der Aufruf eine NotSupportedError-DOMException.
  • Ist mindestens ein Flag true und fehlt message oder ist sie leer, wird ein TypeError geworfen.
  • Ist anchor kein Nachfahre im Shadow Tree des Elements, wird eine NotFoundError-DOMException geworfen.
  • Die Flags werden bei jedem Aufruf komplett ersetzt, nicht zusammengeführt. Wer mehrere Fehler gleichzeitig melden will, übergibt alle Flags in einem Aufruf.
  • Der Gültigkeitszustand ändert sich nicht von selbst. Du musst setValidity() nach jeder Wertänderung, bei formResetCallback() und bei Änderungen relevanter Attribute (z. B. required) erneut aufrufen.
  • Die Meldung wird als Klartext angezeigt, nicht als HTML. Ein XSS-Risiko besteht dadurch nicht. Benutzereingaben in der Meldung solltest du trotzdem kürzen.
  • Elemente mit dem Attribut disabled oder innerhalb eines deaktivierten <fieldset> werden von der Validierung ausgenommen (willValidate ist dann false).
  • Browser-Unterstützung: Chrome/Edge 77+, Firefox 98+, Safari erst ab 16.4. Für ältere Safari-Versionen gibt es das Polyfill element-internals-polyfill.