Wie man ein HTML-Formular mit clientseitiger Validierung in JavaScript erstellt

Ein gut gemachtes Formular ist nicht nur „Felder + Senden“: Es muss den Benutzer leiten, Fehler verhindern und Daten auf klare Weise validieren. In diesem Leitfaden erstellen wir ein HTML-Formular mit clientseitiger Validierung in JavaScript unter Verwendung der Constraint Validation API, benutzerdefinierten Nachrichten und bewährten UX- und Zugänglichkeitsverfahren, mit Code zum Kopieren.

Programmazione JavaScript
Programmazione JavaScript

Clientseitige Validierung dient der Verbesserung der Benutzerfreundlichkeit: Sie meldet Fehler sofort, reduziert unnötige Übermittlungen und macht das Formular „benutzerfreundlicher“. Aber sie ist keine Sicherheitsmaßnahme: Jede Kontrolle muss trotzdem serverseitig wiederholt werden. Abgesehen davon ist eine gut gemachte Client-Validierung eines jener Details, die die Konversionsraten wirklich verändern (und „schmutzige“ Kontakte reduzieren).

In diesem Leitfaden zeigen wir, wie man ein modernes HTML-Formular mit clientseitiger Validierung in JavaScript erstellt, indem man die nativen Browser-Tools (Constraint Validation API) verwendet und personalisierte Nachrichten, Fehlerbehandlung, Zugänglichkeit und eine saubere UX hinzufügt.

Was bedeutet „clientseitige Validierung“

Clientseitige Validierung bedeutet, Daten zu überprüfen, bevor das Formular an den Server gesendet wird. Beispiele:

  • E-Mail-Feld leer oder ungültig
  • Passwort zu kurz
  • Checkbox „Datenschutzbestimmungen akzeptieren“ nicht ausgewählt
  • Telefonnummer mit falschem Format

Der beste Weg, dies zu tun, ist eine Kombination aus:

  • HTML5-Validierung (required, type, minlength, pattern usw.)
  • JavaScript zur Verwaltung von UI, benutzerdefinierten Nachrichten und zusätzlicher Logik

Goldene Regel: clientseitige UX, serverseitige Sicherheit

Denken Sie daran: Der Benutzer kann JS deaktivieren, HTML ändern oder Anfragen direkt über die API senden. Daher:

  • Client-Validierung dient der UX und Datenqualität
  • Server-Validierung dient der Sicherheit und Integrität

Komplettes Beispiel: Formular mit Validierung und benutzerdefinierten Nachrichten

Hier finden Sie ein fertiges Beispiel: Name, E-Mail, Telefon, Nachricht und Datenschutzeinwilligung. Wir verwenden:

  • Constraint Validation API (checkValidity, reportValidity, validity)
  • Fehlermeldungen für einzelne Felder
  • CSS-Klassen für gültigen/ungültigen Status
  • Barrierefreiheit mit aria-describedby und aria-live
<form id="contactForm" novalidate>
  <div class="field">
    <label for="name">Nome e cognome</label>
    <input id="name" name="name" type="text" required minlength="2"
           autocomplete="name" aria-describedby="nameError">
    <p id="nameError" class="error" aria-live="polite"></p>
  </div>

  <div class="field">
    <label for="email">Email</label>
    <input id="email" name="email" type="email" required
           autocomplete="email" aria-describedby="emailError">
    <p id="emailError" class="error" aria-live="polite"></p>
  </div>

  <div class="field">
    <label for="phone">Telefono (opzionale)</label>
    <input id="phone" name="phone" type="tel"
           inputmode="tel"
           pattern="^+?[0-9s-]{7,20}$"
           placeholder="+39 348 123 4567"
           autocomplete="tel" aria-describedby="phoneHelp phoneError">
    <small id="phoneHelp" class="help">Formato ammesso: numeri, spazi e trattini.</small>
    <p id="phoneError" class="error" aria-live="polite"></p>
  </div>

  <div class="field">
    <label for="message">Messaggio</label>
    <textarea id="message" name="message" required minlength="10" rows="5"
              aria-describedby="messageError"></textarea>
    <p id="messageError" class="error" aria-live="polite"></p>
  </div>

  <div class="field">
    <label class="checkbox">
      <input id="privacy" name="privacy" type="checkbox" required aria-describedby="privacyError">
      Ho letto e accetto l’informativa privacy
    </label>
    <p id="privacyError" class="error" aria-live="polite"></p>
  </div>

  <button type="submit">Invia</button>

  <p id="formStatus" class="status" aria-live="polite"></p>
</form>

Minimale Stile (optional), um die Zustände klar darzustellen:

.field { margin-bottom: 14px; }
label { display:block; margin-bottom: 6px; font-weight: 600; }
input, textarea { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 8px; }
input.is-invalid, textarea.is-invalid { border-color: #c0392b; }
input.is-valid, textarea.is-valid { border-color: #27ae60; }
.error { margin: 6px 0 0; color: #c0392b; font-size: 0.95rem; }
.help { display:block; margin-top: 6px; color:#666; font-size: 0.9rem; }
.status { margin-top: 12px; }

Und jetzt der JavaScript-Teil: Validierung beim Absenden + „Live“-Validierung während der Eingabe.

(() => {
  const form = document.getElementById('contactForm');
  const statusEl = document.getElementById('formStatus');

  // Mappa campo -> elemento errori
  const errorMap = new Map([
    ['name', document.getElementById('nameError')],
    ['email', document.getElementById('emailError')],
    ['phone', document.getElementById('phoneError')],
    ['message', document.getElementById('messageError')],
    ['privacy', document.getElementById('privacyError')],
  ]);

  const getErrorMessage = (input) => {
    const v = input.validity;

    if (v.valueMissing) {
      // Messaggi specifici per campo
      if (input.name === 'privacy') return 'Devi accettare l’informativa privacy per proseguire.';
      return 'Questo campo è obbligatorio.';
    }
    if (v.typeMismatch) {
      if (input.type === 'email') return 'Inserisci un indirizzo email valido.';
      return 'Formato non valido.';
    }
    if (v.tooShort) {
      return `Inserisci almeno ${input.minLength} caratteri.`;
    }
    if (v.patternMismatch) {
      if (input.name === 'phone') return 'Inserisci un numero valido (numeri, spazi e trattini).';
      return 'Formato non valido.';
    }
    if (v.badInput) return 'Valore non valido.';
    return '';
  };

  const setFieldState = (input, isValid, message = '') => {
    const errorEl = errorMap.get(input.name);
    if (errorEl) errorEl.textContent = message;

    input.classList.toggle('is-invalid', !isValid);
    input.classList.toggle('is-valid', isValid);

    // Per accessibilità: aria-invalid quando non valido
    input.setAttribute('aria-invalid', String(!isValid));
  };

  const validateField = (input) => {
    // telefono opzionale: se vuoto, consideralo valido
    if (input.name === 'phone' && input.value.trim() === '') {
      setFieldState(input, true, '');
      return true;
    }

    const ok = input.checkValidity();
    const msg = ok ? '' : getErrorMessage(input);
    setFieldState(input, ok, msg);
    return ok;
  };

  const focusFirstInvalid = () => {
    const firstInvalid = form.querySelector('.is-invalid');
    if (firstInvalid) firstInvalid.focus({ preventScroll: false });
  };

  // Validazione live: al blur e all'input
  form.addEventListener('blur', (e) => {
    const target = e.target;
    if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) {
      validateField(target);
    }
  }, true);

  // Debounce semplice per non validare a ogni tasto in modo aggressivo
  let t = null;
  form.addEventListener('input', (e) => {
    const target = e.target;
    if (!(target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement)) return;

    clearTimeout(t);
    t = setTimeout(() => validateField(target), 200);
  });

  form.addEventListener('submit', async (e) => {
    e.preventDefault();
    statusEl.textContent = '';

    const inputs = Array.from(form.querySelectorAll('input, textarea'));
    const allOk = inputs.every(validateField);

    if (!allOk) {
      statusEl.textContent = 'Controlla i campi evidenziati.';
      focusFirstInvalid();
      return;
    }

    // Qui invieresti i dati al server (fetch/AJAX) oppure lasci fare al form
    // Esempio placeholder:
    statusEl.textContent = 'Invio in corso...';

    try {
      // Simulazione invio
      await new Promise((r) => setTimeout(r, 400));
      statusEl.textContent = 'Messaggio inviato correttamente.';
      form.reset();

      // Pulisci stati visivi dopo reset
      inputs.forEach((i) => {
        i.classList.remove('is-valid', 'is-invalid');
        i.removeAttribute('aria-invalid');
        const err = errorMap.get(i.name);
        if (err) err.textContent = '';
      });
    } catch (err) {
      statusEl.textContent = 'Errore durante l’invio. Riprova.';
    }
  });
})();

Warum die Constraint Validation API verwenden?

Viele erfinden das Rad mit Regex überall und manuellen Kontrollen neu. Tatsächlich bieten moderne Browser ein robustes, bereits fertiges System:

  • checkValidity() prüft die Gültigkeit des Feldes, ohne Browser-Popups anzuzeigen
  • reportValidity() kann die nativen Nachrichten anzeigen (nützlich in einfachen Fällen)
  • validityzeigt den detaillierten Status an (valueMissing, tooShort, typeMismatch, patternMismatch…)

So konzentrieren Sie sich auf die UX (Nachrichten, Hervorhebung, Barrierefreiheit), ohne unnötige Logiken zu duplizieren.

„Sanfte“ Validierung: Wann der Fehler angezeigt werden soll

Ein zu früh angezeigter Fehler kann störend sein. Praktische Richtlinien:

  • Fehler anzeigen beim Absenden (immer)
  • Fehler anzeigen beim Blur (wenn der Benutzer das Feld verlässt)
  • „live“ während der Eingabe nur mit Debounce und nur validieren, wenn das Feld bereits berührt wurde (fortschrittlicherer Ansatz)

Im Beispiel validieren wir beim Absenden alles; während der Eingabe validieren wir beim Blur und mit einem leichten Debounce.

Barrierefreiheit: Keine Option

Zwei Punkte machen den Unterschied:

  • Verknüpfen Sie den Fehlertext mit dem Feld mit aria-describedby
  • Aktualisieren Sie den Status mit aria-invalid bei Bedarf

Zusätzlich ermöglicht die Verwendung von aria-live="polite" auf dem Fehlertext Screenreadern, die Änderung zu kommunizieren.

Schnelle FAQs

Muss ich novalidate im Formular verwenden?

Wenn Sie Nachrichten und UI selbst verwalten möchten, ja: novalidate deaktiviert die nativen Browser-Popups und ermöglicht Ihnen eine konsistente UX. Wenn die Standard-HTML5-Validierung ausreicht, können Sie es auch weglassen.

Ist der Regex für die Telefonnummer obligatorisch?

Nein. Telefonnummern sind komplex (verschiedene Formate). Besser ist eine permissive clientseitige Validierung und serverseitige Normalisierung, oder dedizierte Bibliotheken, wenn hohe Präzision erforderlich ist.

Kann ich mit fetch senden, ohne die Seite neu zu laden?

Ja. Im Submit-Block finden Sie bereits eine Stelle, an der Sie fetch() einfügen können. Denken Sie daran: immer serverseitige Validierung, Fehlerbehandlung und klare Nachrichten clientseitig.

Fazit

Ein Formular mit clientseitiger Validierung in JavaScript muss nicht kompliziert sein: HTML5 liefert bereits solide Regeln, JavaScript ermöglicht es Ihnen, diese klarer, konsistenter und zugänglicher zu gestalten. Wenn Sie nützliche Nachrichten erstellen, Fehler richtig hervorheben und die Barrierefreiheit nicht vergessen, erhalten Sie schnellere Formulare, weniger Frustration und bessere Datenqualität.

Pubblicato in ,

Se vuoi rimanere aggiornato su Wie man ein HTML-Formular mit clientseitiger Validierung in JavaScript erstellt iscriviti alla nostra newsletter settimanale

Hinterlasse jetzt einen Kommentar

Kommentar hinterlassen

E-Mail Adresse wird nicht veröffentlicht.


*