Form Validation Teil 1: Constraint Validation in HTML

Avatar of Chris Ferdinandi
Chris Ferdinandi am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Die meisten JavaScript-Bibliotheken zur Formularvalidierung sind groß und erfordern oft andere Bibliotheken wie jQuery. So enthält beispielsweise das einbettbare Formular von MailChimp eine 140 KB große Validierungsdatei (minifiziert). Sie enthält die gesamte jQuery-Bibliothek, ein Formularvalidierungs-Plugin von Drittanbietern und benutzerdefinierten MailChimp-Code. Genau diese Konfiguration hat mich zu dieser neuen Serie über moderne Formularvalidierung inspiriert. Welche neuen Werkzeuge haben wir heutzutage zur Formularvalidierung? Was ist möglich? Was wird noch benötigt?

In dieser Serie zeige ich Ihnen zwei leichte Möglichkeiten, Formulare im Frontend zu validieren. Beide nutzen neuere Web-APIs. Außerdem zeige ich Ihnen, wie Sie die Browserunterstützung für diese APIs bis IE9 zurückführen können (was Ihnen eine Abdeckung von 99,6 % des weltweiten Webverkehrs verschafft).

Schließlich werfen wir einen Blick auf das Anmeldeformular von MailChimp und bieten die gleiche Erfahrung mit 28-mal weniger Code.

Es ist erwähnenswert, dass die Validierung von Formularen im Frontend umgangen werden kann. Sie sollten Ihren Code immer auch auf dem Server validieren.

Okay, legen wir los!

Artikelserie

  1. Constraint Validation in HTML (Sie sind hier!)
  2. Die Constraint Validation API in JavaScript
  3. Ein Validity State API Polyfill
  4. Validierung des MailChimp-Anmeldeformulars

Der unglaublich einfache Weg: Constraint Validation

Durch eine Kombination aus semantischen Eingabetypen (z. B. <input type="email">) und Validierungsattributen (wie required und pattern) können Browser Formulareingaben nativ validieren und Benutzer benachrichtigen, wenn sie etwas falsch machen.

Die Unterstützung für die verschiedenen Eingabetypen und Attribute unterscheidet sich von Browser zu Browser stark, aber ich werde einige Tricks und Workarounds bereitstellen, um die Browserkompatibilität zu maximieren.

Grundlegende Textvalidierung

Nehmen wir an, Sie haben ein Textfeld, das vom Benutzer ausgefüllt werden muss, bevor das Formular gesendet werden kann. Fügen Sie das Attribut required hinzu, und unterstützte Browser benachrichtigen die Benutzer, die es nicht ausfüllen, und verhindern, dass sie das Formular absenden.

<input type="text" required>
Ein erforderliches Textfeld in Chrome.

Benötigen Sie, dass die Antwort eine Mindest- oder Höchstanzahl von Zeichen hat? Verwenden Sie minlength und maxlength, um diese Regeln durchzusetzen. Dieses Beispiel erfordert einen Wert zwischen 3 und 12 Zeichen.

<input type="text" minlength="3" maxlength="12">
Fehlermeldung für die falsche Zeichenanzahl in Firefox.

Das Attribut pattern ermöglicht es Ihnen, Regex-Validierungen gegen Eingabewerte durchzuführen. Wenn Sie zum Beispiel möchten, dass Passwörter mindestens 1 Großbuchstaben, 1 Kleinbuchstaben und 1 Zahl enthalten, kann der Browser dies für Sie validieren.

<input type="password" pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$" required>
Fehlermeldung für falsches Format in Safari.

Wenn Sie ein title-Attribut mit dem pattern angeben, wird der Wert des title in jede Fehlermeldung aufgenommen, wenn das Muster nicht übereinstimmt.

<input type="password" pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$" title="Please include at least 1 uppercase character, 1 lowercase character, and 1 number." required>
Fehlermeldung für falsches Format in Opera, mit Titeltext, der RegEx erklärt.

Sie können es sogar mit minlength und (wie bei Banken anscheinend üblich) maxlength kombinieren, um eine Mindest- oder Höchstlänge zu erzwingen.

<input type="password" minlength="8" pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$" title="Please include at least 1 uppercase character, 1 lowercase character, and 1 number." required>

Siehe den Stift Form Validation: Basic Text von Chris Ferdinandi (@cferdinandi) auf CodePen.

Validierung von Zahlen

Der Eingabetyp number akzeptiert nur Zahlen. Browser weigern sich entweder, Buchstaben und andere Zeichen zu akzeptieren, oder sie benachrichtigen die Benutzer, wenn sie diese verwenden. Die Browserunterstützung für input[type="number"] variiert, aber Sie können als Fallback ein pattern angeben.

<input type="number" pattern="[-+]?[0-9]">

Standardmäßig erlaubt der Eingabetyp number nur ganze Zahlen.

Sie können Fließkommazahlen (Zahlen mit Dezimalstellen) mit dem Attribut step zulassen. Dies teilt dem Browser mit, welches numerische Intervall akzeptiert werden soll. Es kann jeder numerische Wert sein (Beispiel: 0.1) oder any, wenn Sie jede Zahl zulassen möchten.

Sie sollten auch Ihr pattern anpassen, um Dezimalzahlen zuzulassen.

<input type="number" step="any" pattern="[-+]?[0-9]*[.,]?[0-9]+">

Wenn die Zahlen zwischen einem bestimmten Wertebereich liegen sollen, können die Browser dies mit den Attributen min und max validieren. Sie sollten auch Ihr pattern anpassen, um dies zu berücksichtigen. Wenn zum Beispiel eine Zahl zwischen 3 und 42 liegen muss, würden Sie dies tun

<input type="number" min="3" max="42" pattern="[3-9]|[1-3][0-9]|4[0-2]">

Siehe den Stift Form Validation: Numbers von Chris Ferdinandi (@cferdinandi) auf CodePen.

Validierung von E-Mail-Adressen und URLs

Der Eingabetyp email benachrichtigt die Benutzer, wenn die angegebene E-Mail-Adresse ungültig ist. Wie beim Eingabetyp number sollten Sie ein Muster für Browser angeben, die diesen Eingabetyp nicht unterstützen.

Regex-Muster für die E-Mail-Validierung sind ein heiß diskutiertes Thema. Ich habe viele davon getestet, insbesondere solche, die den RFC822-Spezifikationen entsprechen. Das unten verwendete Muster von Richard Willis war das beste, das ich finden konnte.

<input type="email" pattern="^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*$">

Ein "Haken" beim Eingabetyp email ist, dass er E-Mail-Adressen ohne TLD (den Teil "example.com" von "[email protected]") zulässt. Das liegt daran, dass RFC822, der Standard für E-Mail-Adressen, E-Mails für lokale Netzwerke zulässt, die keine benötigen.

Wenn Sie eine TLD verlangen möchten (und das tun Sie wahrscheinlich), können Sie das pattern so anpassen, dass eine Domain-Erweiterung erzwungen wird:

<input type="email" title="The domain portion of the email address is invalid (the portion after the @)." pattern="^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*(\.\w{2,})+$">

Ebenso benachrichtigt der Eingabetyp url die Benutzer, wenn der angegebene Wert keine gültige URL ist. Auch hier sollten Sie ein Muster für Browser angeben, die diesen Eingabetyp nicht unterstützen. Das unten angegebene wurde aus einem Projekt von Diego Perini übernommen und ist das robusteste, das ich bisher gesehen habe.

<input type="url" pattern="^(?:(?:https?|HTTPS?|ftp|FTP):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)(?:\.(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)*)(?::\d{2,})?(?:[\/?#]\S*)?$">

Wie das email-Attribut erfordert url keine TLD. Wenn Sie keine lokalen URLs zulassen möchten, können Sie das Muster so aktualisieren, dass es eine TLD prüft, wie hier:

<input type="url" title="The URL is a missing a TLD (for example, .com)." pattern="^(?:(?:https?|HTTPS?|ftp|FTP):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)(?:\.(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)*(?:\.(?:[a-zA-Z\u00a1-\uffff]{2,}))\.?)(?::\d{2,})?(?:[/?#]\S*)?$">

Siehe den Stift Form Validation: Email & URLs von Chris Ferdinandi (@cferdinandi) auf CodePen.

Validierung von Datumsangaben

Es gibt einige wirklich großartige Eingabetypen, die nicht nur Datumsangaben validieren, sondern auch native Datums-Pickere bereitstellen. Leider sind Chrome, Edge und Mobile Safari die einzigen Browser, die dies implementieren. (Ich warte schon seit Jahren darauf, dass Firefox dieses Feature übernimmt! Update: Dieses Feature soll hoffentlich bald auch in Firefox Einzug halten.) Andere Browser zeigen es nur als Textfeld an.

Wie immer können wir ein pattern bereitstellen, um Browser zu erfassen, die dies nicht unterstützen.
Der Eingabetyp date ist für Standard-Tag/Monat/Jahr-Datumsangaben.

<input type="date" pattern="(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))">

In unterstützten Browsern wird das ausgewählte Datum so angezeigt: MM/TT/JJJJ (Vorbehalt: in den USA. Dies kann für Benutzer in anderen Ländern oder mit geänderten Datumseinstellungen variieren). Der value ist jedoch tatsächlich in diesem Format: JJJJ-MM-TT.

Sie sollten Benutzer von nicht unterstützten Browsern über dieses Format informieren, z. B. "Bitte verwenden Sie das Format JJJJ-MM-TT". Sie möchten jedoch nicht, dass Personen, die Chrome oder Mobile Safari verwenden, dies sehen, da dies nicht das Format ist, das sie sehen, was verwirrend ist.

Siehe den Stift Form Validation: Dates von Chris Ferdinandi (@cferdinandi) auf CodePen.

Ein einfacher Feature-Test

Wir können jedoch einen einfachen Feature-Test schreiben, um die Unterstützung zu prüfen. Wir erstellen ein Element vom Typ input[type="date"], fügen einen Wert hinzu, der kein gültiges Datum ist, und sehen dann, ob der Browser ihn bereinigt oder nicht. Sie können dann den beschreibenden Text für Browser ausblenden, die den Eingabetyp date unterstützen.

<label for="date">Date <span class="description-date">YYYY-MM-DDD</span></label>
<input type="date" id="date" pattern="(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))">

<script>
  var isDateSupported = function () {
      var input = document.createElement('input');
      var value = 'a';
      input.setAttribute('type', 'date');
      input.setAttribute('value', value);
      return (input.value !== value);
  };

  if (isDateSupported()) {
      document.documentElement.className += ' supports-date';
  }
</scipt>

<style>
  .supports-date .description-date {
      display: none;
  }
</style>

Siehe den Stift Form Validation: Dates with a Feature Test von Chris Ferdinandi (@cferdinandi) auf CodePen.

Andere Datumstypen

Der Eingabetyp time ermöglicht es Besuchern, eine Uhrzeit auszuwählen, während der Eingabetyp month eine Auswahl von Monat/Jahr ermöglicht. Wiederum fügen wir ein Muster für nicht unterstützende Browser hinzu.

<input type="time" pattern="(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9])">
<input type="month" pattern="(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2]))">

Der Eingabetyp time zeigt die Uhrzeit im 12-Stunden-AM/PM-Format an, aber der value ist die 24-Stunden-Militärzeit. Der Eingabetyp month wird in unterstützten Browsern als Mai 2017 angezeigt, aber der Wert ist im Format JJJJ-MM.

Genau wie bei input[type="date"] sollten Sie eine Musterbeschreibung bereitstellen, die in unterstützten Browsern ausgeblendet ist.

Siehe den Stift Form Validation: Add `novalidate` programmatically von Chris Ferdinandi (@cferdinandi) auf CodePen.

Das scheint super einfach. Wo ist der Haken?

Obwohl die Constraint Validation API einfach und leichtgewichtig ist, hat sie einige Nachteile.

Sie können Felder, die Fehler enthalten, mit der Pseudo-Klasse :invalid stylen, aber Sie können die Fehlermeldungen selbst nicht stylen.

Das Verhalten ist auch browserübergreifend inkonsistent. Chrome zeigt keine Fehler an, bis Sie versuchen, das Formular abzuschicken. Firefox zeigt einen roten Rahmen an, wenn das Feld den Fokus verliert, zeigt aber Fehlermeldungen nur beim Überfahren mit der Maus an (während WebKit-Browser die Fehler dauerhaft anzeigen).

Benutzerstudien von Christian Holst und Luke Wroblewski (separat) haben ergeben, dass die Anzeige eines Fehlers, wenn der Benutzer ein Feld verlässt, und die Beibehaltung dieses Fehlers, bis das Problem behoben ist, das beste und schnellste Benutzererlebnis bietet. Zusätzlicher CSS-Tipp: Ungültige Selektoren nur stylen, wenn sie nicht gerade bearbeitet werden, mit :not(:focus):invalid { }.

Leider verhalten sich standardmäßig keiner der Browser genau so.

Im nächsten Artikel dieser Serie zeige ich Ihnen, wie Sie die native Constraint Validation API verwenden, um unser gewünschtes UX mit leichtgewichtigem JavaScript zu ergänzen. Keine Drittanbieter-Bibliothek erforderlich!

Artikelserie

  1. Constraint Validation in HTML (Sie sind hier!)
  2. Die Constraint Validation API in JavaScript
  3. Ein Validity State API Polyfill
  4. Validierung des MailChimp-Anmeldeformulars