Eingabe nach Interaktion validieren

Das Problem

Validierungsfehler anzuzeigen, sobald ein Nutzer ein Feld auswählt und mit der Eingabe beginnt, ist verfrüht und ablenkend. Wenn ein Nutzer beispielsweise eine E‑Mail-Adresse (z.B. „nutzer@gm“) oder ein Passwort mit komplexen Anforderungen eingibt, ist das Feld technisch gesehen ungültig, bis die Eingabe abgeschlossen ist. Bei der Standard-:invalid-Stilgestaltung wird sofort ein Fehlerstatus angezeigt, was für den Nutzer frustrierend ist.

Die Lösung

Mit der Pseudoklasse :user-invalid können Sie den Fehlerstatus so lange aufschieben, bis der Nutzer einen Wert „bestätigt“ hat (indem er das Feld verlässt) oder versucht hat, das Formular zu senden. So wird sichergestellt, dass Validierungsfeedback erst dann angezeigt wird, wenn der Nutzer die Interaktion mit dem Feld abgeschlossen hat.

Implementierungsstrategie

  1. HTML-Einschränkung: Verwenden Sie Standard-HTML5-Attribute wie type="email", pattern und required, um die integrierte Validierungslogik des Browsers auszulösen.
  2. Visuelles Feedback: Verwenden Sie :user-invalid, um das Fehler-Styling erst nach der Interaktion anzuwenden.
  3. Positive Verstärkung: Sie können optional :user-valid verwenden, um eine grüne Erfolgsanzeige einzublenden, sobald die Anforderungen erfüllt sind.
  4. Fehlerbehebung: Sobald der Nutzer die Eingabe in ein gültiges Format korrigiert, wird :user-invalid nicht mehr ausgelöst und der Fehlerstatus wird sofort entfernt.

Implementierungsleitfaden

Anwendungsfall 1: E‑Mail-Validierung

OBLIGATORISCH: Verwenden Sie für E-Mail-Felder Standard-HTML5-Attribute. Die Fehlermeldung ist standardmäßig ausgeblendet und wird nur angezeigt, wenn der Browser feststellt, dass der Nutzer das Feld in einem ungültigen Zustand verlassen hat.

<form>
  <div class="field">
    <label for="email">Email Address</label>
    <!-- MANDATORY: Place format hints above the input so autocomplete popovers don't cover them during editing -->
    <span id="email-hint" class="hint">Format: you@example.com</span>
    <!-- DO: Use standard HTML validation attributes like type="email" and required -->
    <input
      type="email"
      id="email"
      name="email"
      required
      autocomplete="email"
      aria-describedby="email-hint"
      aria-errormessage="email-error"
    >
    <div id="email-error" class="error-msg">
      <span aria-hidden="true">❌</span> Please enter a valid email address.
    </div>
  </div>
</form>
.hint {
  display: block;
  color: #5f6368;
  font-size: 0.85rem;
  margin-bottom: 0.25rem;
}

.error-msg {
  display: none;
  color: #d93025;
  font-size: 0.875rem;
  margin-top: 0.25rem;
}

/*
  DO: Only show error styles after user interaction.
  Use multiple indicators (border/background shift + icon/text) to avoid color-only states.
*/
input:user-invalid {
  border-color: #d93025;
  background-color: #fce8e6;
}

/* DO: Reveal the error message using the adjacent sibling selector */
input:user-invalid + .error-msg {
  display: block;
}

/* DO: Provide a clear success indication on :user-valid */
input:user-valid {
  border-color: #188038;
}

Anwendungsfall 2: Passwortkomplexität

OBLIGATORISCH: Definieren Sie die Komplexitätsregel mit einem Regex-Lookahead-Muster im Attribut pattern. Die Liste der Regeln wird über der Eingabe angezeigt, um den Nutzer zu unterstützen, und bei einem Fehler hervorgehoben.

<form>
  <div class="field">
    <label for="password">New Password</label>
    <!-- MANDATORY: Place hints and rules above the input so mobile keyboards do not obscure them -->
    <ul id="password-rules" class="rules-list">
      <li>At least 8 characters</li>
      <li>One uppercase letter</li>
      <li>One number</li>
      <li>One special character</li>
    </ul>
    <!-- DO: Use pattern and minlength for complex password validation
         DO: Match all constraints with lookaheads via pattern attribute
     -->
    <input
      type="password"
      id="password"
      autocomplete="new-password"
      required
      pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{8,}"
      minlength="8"
      aria-describedby="password-rules"
    >
  </div>
</form>
/* DO: State the default styling as neutral */
.rules-list { 
  color: #5f6368; 
  margin-bottom: 0.5rem;
}

/* DO: Show invalid state (After interaction): Error */
input:user-invalid {
  border-color: #d93025;
  background-color: #fce8e6;
}

/* DO: Highlight rules list when error is shown using the modern :has() selector */
.field:has(input:user-invalid) .rules-list {
  color: #d93025;
  font-weight: 600;
}

/* DO: Add success indications for :user-valid state */
input:user-valid {
  border-color: #188038;
}

/* DO: Hide rules once satisfied */
.field:has(input:user-valid) .rules-list {
  display: none;
}

Fallback und Browserunterstützung

Fallbacks und Browserunterstützung für :user-valid und :user-invalid

CSS für Fallback

input:user-invalid,
input.user-invalid-fallback {
  border-color: #d93025;
  background-color: #fce8e6;
}

input:user-invalid + .error-msg,
input.user-invalid-fallback + .error-msg {
  display: block;
}

JavaScript-Fallback

Verwenden Sie ein wiederverwendbares Dienstprogramm, das den Interaktionsstatus mithilfe eines WeakMap verfolgt. So wird verhindert, dass das DOM mit „schmutzigen“ Klassen oder Datenattributen verunreinigt wird.

const UserInvalidFallback = (() => {
  const dirtyState = new WeakMap();

  const updateState = (input) => {
    const isValid = input.checkValidity();

    // Update both visual and ARIA state
    input.classList.toggle('user-invalid-fallback', !isValid);
    input.classList.toggle('user-valid-fallback', isValid);

    if (!isValid) {
      input.setAttribute('aria-invalid', 'true');
    } else {
      input.removeAttribute('aria-invalid');
    }
  };

  const handleEvent = (event) => {
    const input = event.target;

    if (event.type === 'reset') {
      const controls = input.elements || [];
      for (const control of controls) {
        dirtyState.delete(control);
        control.classList.remove('user-invalid-fallback');
        control.classList.remove('user-valid-fallback');
        control.removeAttribute('aria-invalid');
      }
      return;
    }

    if (!input.checkValidity) return;

    if (event.type === 'input' || event.type === 'change') {
      const state = dirtyState.get(input) || { hasInteracted: false, hasBlurred: false };
      state.hasInteracted = true;
      dirtyState.set(input, state);
      if (state.hasBlurred) {
        updateState(input);
      }
    } else if (event.type === 'blur') {
      const state = dirtyState.get(input) || { hasInteracted: false, hasBlurred: false };
      state.hasBlurred = true;
      dirtyState.set(input, state);
      if (state.hasInteracted) {
        updateState(input);
      }
    }
  };

  const init = (root = document) => {
    if (CSS.supports('selector(:user-invalid)')) return;

    root.addEventListener('blur', handleEvent, true); // Capture phase
    root.addEventListener('input', handleEvent);
    root.addEventListener('change', handleEvent);
    root.addEventListener('reset', handleEvent, true); // Capture resets
  };

  return { init };
})();

// Initialize for a specific form
const form = document.querySelector('#demo-form');
UserInvalidFallback.init(form);

Weitere Überlegungen

  1. Barrierefreiheit im Internet:
    • OBLIGATORISCH: Verwenden Sie aria-describedby, um die Regelliste mit der Eingabe zu verknüpfen.
    • NICHT: Verbergen Sie die Regellisten nicht vollständig, bis die Eingabe gültig ist. Die Nutzer müssen wissen, was sie eingeben sollen.
  2. Einschränkungen für Musterattribute: OBLIGATORISCH: Das Attribut pattern führt einen vollständigen Abgleich aus (implizit ^...$). Achten Sie darauf, dass der reguläre Ausdruck für das Passwort den gesamten String berücksichtigt.
  3. Strenge der Validierung: Beachten Sie, dass die standardmäßige type="email"-Validierung des Browsers recht tolerant ist (z.B. wird user@localserver möglicherweise akzeptiert). Wenn Sie eine strengere Validierung benötigen, müssen Sie möglicherweise eine robustere Validierungsbibliothek oder eine benutzerdefinierte Validierungsfunktion zusammen mit type="email" verwenden.
  4. Fokusverwaltung: OBLIGATORISCH: Wenn ein Nutzer das Formular mit einem ungültigen Feld absendet, wird der Fokus im Browser automatisch auf das erste ungültige Feld gesetzt. Ihre :user-invalid-Stile werden sofort angewendet, da ein Absendeversuch als Interaktion zählt.
  5. Konsistente ARIA-Nutzung: Native :user-invalid-Elemente werden nicht automatisch mit ARIA-Attributen synchronisiert. Fügen Sie den folgenden JavaScript-Code hinzu, um aria-invalid mit dem visuellen Status synchron zu halten:
// Sync aria-invalid with the CSS :user-invalid state
const syncAria = (el) => {
  el.setAttribute?.('aria-invalid', el.matches(':user-invalid') ? 'true' : 'false');
};

// Update on blur (to show error) and input (to clear it)
document.addEventListener('blur', (e) => syncAria(e.target), true);
document.addEventListener('input', (e) => {
  if (e.target.hasAttribute('aria-invalid')) syncAria(e.target);
});