Die unzureichende Nutzung der HTML-Formularvalidierung
(expressionstatement.com)- HTML-Formulare verfügen bereits über native Validierungsfunktionen wie
required,type="email",pattern,maxlengthundsetCustomValidity, doch in der Praxis werden sie gemessen am Funktionsumfang wenig genutzt - Das besonders mächtige
setCustomValiditykann beliebige Validierungslogik und komplexe Fälle abdecken, wird aber nur als DOM-Methode bereitgestellt und passt daher schlecht zu deklarativen Komponenten - In Umgebungen wie React greifen für eine korrekte Validierung von Anfangswerten
useRef,useLayoutEffectundonChangeineinander, und dieselbe Validierungslogik wird leicht beim initialen Rendern und im Change-Handler dupliziert - Mit einer attributbasierten Abstraktion wie
custom-validityließen sich Validierungen mit Abhängigkeiten zwischen Zustand und Eingabe, etwa asynchrone Prüfungen auf doppelte Benutzernamen oder Passwortbestätigungen, an einer Stelle ausdrücken - Die geringe Verbreitung nativer Formularvalidierung liegt weniger an fehlenden Funktionen als an der Benutzbarkeit der API; eine deklarative API in der HTML-Spezifikation würde deutlich mehr Einsatzmöglichkeiten eröffnen
Was HTML-Formularvalidierung bereits bietet
- Die einfachste Möglichkeit, leere Eingaben zu verhindern, ist das Hinzufügen des Attributs
required - Eingabebeschränkungen lassen sich grob auf drei Arten setzen
- Verwendung von Eingabetypen wie
type="email",type="number"odertype="url" - Verwendung von Constraint-Attributen wie
patternundmaxlength - Verwendung der DOM-Methode
setCustomValidityeines Eingabefelds
- Verwendung von Eingabetypen wie
setCustomValidityist das mächtigste Mittel, weil es beliebige Validierungslogik und komplexe Fälle behandeln kann- Die ersten beiden Ansätze lassen sich als HTML-Attribute deklarieren;
setCustomValidityunterscheidet sich dadurch, dass ein Methodenaufruf erforderlich ist - Als Referenz zum Unterschied zwischen DOM-Attributen und Properties ist Attributes vs Properties verlinkt
Wo setCustomValidity unbequem wird
setCustomValiditywird ausschließlich als Methode ohne entsprechendes HTML-Attribut bereitgestellt- Übergibt man einen String, wird die Eingabe in den Zustand invalid versetzt, und der Browser zeigt diesen String als Grund für die fehlgeschlagene Validierung an
- Übergibt man einen leeren String, wird die Eingabe valid, sofern keine anderen Constraints greifen
- Um ein Verhalten wie bei
requiredselbst zu implementieren, muss bei jeder WertänderungsetCustomValidityaufgerufen werden - Allerdings ist eine Eingabe anfangs im Zustand valid; wenn man direkt nach dem Zurücksetzen einer Komponente den Submit-Button drückt, kann die Formularübermittlung durchgehen
- Denn selbst bei leerem Wert wird der Validierungscode erst ausgeführt, wenn ein Change-Event auftritt
- Um auch den Anfangswert korrekt zu erfassen, muss dieselbe Validierung bereits beim Mounten der Komponente ausgeführt werden
Mehr Boilerplate in deklarativen Komponenten
- Wenn auch Anfangswerte validiert werden sollen, wird der Code schnell repetitiv und unhandlich
- Besonders drei Probleme entstehen dabei
- Die Validierungslogik wird im
onChange-Handler und in der initialen Renderphase dupliziert - Der initiale Validierungscode liegt vom Eingabeelement entfernt, was die Kohäsion verringert und das Risiko erhöht, nur eine Seite anzupassen
- Die Kombination aus
useRef,useLayoutEffectundonChangewird mit zunehmender Zahl von Eingaben überdimensioniert und ist besonders verwirrend, wenn nur einige FeldercustomValidityverwenden
- Die Validierungslogik wird im
- Diese Komplexität fällt besonders auf, wenn man eine rein imperative API in deklarativen Komponenten verwendet
- Für
CustomValiditygibt es kein Eingabeattribut, mit dem sich ein Wert deklarativ setzen ließe wie bei nativen Validierungsattributen - Wenn eine API umständlich ist, kann ihre Verbreitung trotz starker Funktionen gering bleiben; genau das ist ein zentraler Grund für die unzureichende Nutzung nativer Formularvalidierung
Das fehlende Puzzleteil: ein custom-validity-Attribut
- Benötigt wird eine deklarative Eigenschaft wie
custom-validity, die sich direkt am Eingabefeld angeben lässt - In deklarativen Frameworks könnte der Validierungszustand damit nahe am Eingabeelement platziert werden
- In der aktuellen
HTML Specgibt es kein echtescustom-validity - Für Demo-Zwecke lässt sich dieses Verhalten mit einer Implementierung im Userland nachahmen
- Als Beispiel für eine produktionsreife Komponente wird eine weiter ausgereifte Implementierung bereitgestellt
Asynchrone Validierung und Abhängigkeiten zwischen Eingaben
- Validierung in realen Anwendungen kann komplexer sein als lokale Prüfungen
- Bei einem Benutzernamen muss der Server prüfen, ob der Name bereits verwendet wird; während die Anfrage läuft, sollte das Formular nicht valid sein
- Das Beispiel nutzt
required, um leere Eingaben zu verhindern, und versetzt das Eingabefeld je nach Ladezustand und Antwortergebnis übercustomValidityin den Zustand invalid - Die Implementierung besteht aus zwei Teilen
- Verwaltung des Anfragezustands zur Prüfung der Eindeutigkeit des Benutzernamens mit
useQueryausreact-query - Verwendung einer benutzerdefinierten
<Input />-Komponente, die einecustomValidity-Prop entgegennehmen kann
- Verwaltung des Anfragezustands zur Prüfung der Eindeutigkeit des Benutzernamens mit
- Dieser Ansatz kann den gesamten asynchronen Validierungsfluss einschließlich Lade-, Fehler- und Erfolgszustand in einer einzigen Eigenschaft ausdrücken
- Ein weiteres Beispiel ist ein Formular, das die erneute Eingabe eines Passworts verlangt und damit die Validierung voneinander abhängiger Eingabefelder behandelt
Fazit
setCustomValidityist ein mächtiges Werkzeug, das verschiedenste Validierungsanforderungen abdecken kann- Entscheidend für die tatsächliche Nutzung ist nicht nur, dass eine Funktion existiert, sondern auch eine API, die ihre Verwendung einfach macht
- Eine deklarative Abstraktion wie
custom-validitywürde native Formularvalidierung natürlicher nutzbar machen - Es ist zu hoffen, dass eine solche Funktion eines Tages nativ in die HTML-Spezifikation aufgenommen wird
1 Kommentare
Meinungen auf Hacker News
Als ich zuletzt nachgesehen habe, konnten moderne Webbrowser das Aussehen der eingebauten HTML-Validierungsmeldungen immer noch nicht stylen: https://stackoverflow.com/questions/5328883/how-do-i-style-t...
Wäre es wenigstens so, dass Chrome und Firefox den UI-Richtlinien der jeweiligen OS-Plattform folgten und die Meldungen wie systemeigene Tooltips à la
title=""wirkten, wäre es weniger schlimm. Chrome verwendet aber ein gelb/oranges Icon, schwarzen Text auf weißem Hintergrund und eine Sprechblase mit fest abgerundeten Ecken, was stark mit der Ästhetik des aktuellen Projekts kollidiert.Früher erlaubte Chrome das Styling von Validierungsmeldungen über vendor-präfixierte Pseudoelement-Selektoren, entfernte diese Funktion aber und brachte sie nie zurück. Das landet damit auf meiner willkürlichen Liste von Ärgernissen wie „Gebt mir bitte eine native HTML-Combobox“ und „Warum ist das immer noch eine schwer bedienbare Strg-Klick-Box statt einer Liste von Checkboxen?“
validity-Status eines Eingabefelds auszulesen und nach eigenen Vorstellungen zu rendern.Das eigentliche Problem ist, Änderungen dieses Gültigkeitsstatus zuverlässig zu abonnieren. Es gibt zwar Validitäts-Events, aber sie werden nicht immer ausgelöst. Wenn man den Formularzustand programmatisch ändert, etwa mit
form.reset()oderinput.value = '...', werden diese Events nicht getriggert.Ich halte das für ein gutes Thema, das man gesondert untersuchen und der Webplattform vorschlagen könnte.
selectnicht so einfach, wie es eigentlich sein sollte.Das Größte und am leichtesten Umzusetzende, das trotzdem zu selten genutzt wird, ist die Verwendung konkreter Werte für das
type-Attribut wieemail,numberoderurl.Auf Mobilgeräten kann dadurch die passende Tastatur angezeigt werden, was die User Experience deutlich verbessert.
type=numberund nutze stattdessentype=text inputmode=numeric. So erscheinen keine Pfeil-Buttons, die die meisten Nutzer für die Zahleneingabe nicht brauchen, und unter iOS ist auch die Tastatur besser.typesetzen.date-Eingaben habe ich das ebenfalls sehr oft gesehen. Jede Frontend-Bibliothek hat ihr eigenes Datums-Widget, und viele davon sehen auf kleinen Bildschirmen miserabel aus.Für dieses eine Widget muss man zusätzlich JS und CSS einbinden, manche hängen sogar von jQuery ab. Natürlich gibt es Varianten mit sehr vielen Einstellungsmöglichkeiten, aber für sehr geringe Kosten bekommt man ein Widget, das überall okay ist und nativ wirkt, die Anforderungen meist erfüllt und um das man sich ohne Update- oder CDN-Sorgen nicht weiter kümmern muss.
Mit normalen Eingaben ist das gerade noch machbar, aber spezielle Eingaben unterstützen noch weniger Events.
Mein Produkt hat ein Accessibility-Audit nicht bestanden, weil es native HTML-Formularvalidierung verwendet, und die offizielle Empfehlung war, eine eigene Validierungsschicht zu implementieren. Dieser Empfehlung stimme ich auch zu.
Die native HTML-Validierung hat viele Schwächen, und ehrlich gesagt ist die visuelle Anpassbarkeit nicht einmal die größte Sorge. Sie ist allerdings so etwas wie der letzte Sargnagel.
Wenn man zum Beispiel mehrere Fehler pro Feld auf einmal anzeigen will, bleibt einem nur, Strings aneinanderzuhängen. Etwa: „Eine Zahl ist erforderlich. Ein Symbol ist erforderlich. Muss länger als 10 Zeichen sein.“ Das ist eine schlechte User Experience und auch schlecht für die Accessibility, weil man den zusammengehängten String im Accessibility Tree nicht navigieren kann. Das ist kein Implementierungsproblem, sondern ein Problem der Spezifikation selbst. Es macht keinen Spaß, wenn Nutzer Validierungsfehler wie beim Whac-a-Mole abarbeiten müssen, also sollte man mehrere Fehler auf einmal anzeigen können.
Schlecht ist auch die Browser-Abhängigkeit. Man hat keine Kontrolle, und die Implementierungen sind meist miserabel. Chrome zeigt beim Fokus ein Popup an; es ist ein Popup, sieht wie ein Modal aus und kann nicht alle Formularfehler auf einmal anzeigen, weshalb es an sich schon nicht besonders barrierefrei ist. Wichtige Informationen nicht in Popups anzuzeigen, gehört zu den Accessibility-Grundlagen, aber Browser können nicht viel anderes tun, ohne das eigentliche Dokument zu stören.
Formularweite Fehler, die keinem bestimmten Feld zugeordnet sind, benötigen weiterhin Custom Validation. Etwa Fehler wie „Feld A und Feld B sind nicht miteinander vereinbar“, und dann ist es besser, gleich einen konsistenten Validierungsansatz zu haben.
Wenn man Custom Inputs hat, die nicht zu nativen Eingabetypen passen, braucht man aus Konsistenzgründen versteckte Inputs, was wiederum die Accessibility kaputtmacht. Das zu beheben ist am Ende genauso schwierig, wie gleich ein eigenes barrierefreies Validierungssystem zu bauen.
Die
customValidity-API ist imperativ und umständlich zu benutzen. Wenn man keine schrecklichen Meldungen wie „Dieses Feld ist ungültig“ haben will, ist es praktisch keine Option, auf Custom Validity zu verzichten. Deshalb ist HTML-Formularvalidierung schlecht.Trotzdem halte ich es für sinnvoll, den nativen Validierungsmechanismus zu nutzen. Man muss für Fehlermeldungen nicht zwingend die nativen Validity-Tooltips verwenden; man kann
ValidityStateausinput.validitydirekt auslesen, die Meldungen nach Wunsch rendern und bei Bedarf mehrere Fehler anzeigen.Browser können verbessert werden, und wenn man einen standardisierten Ansatz nutzt, kann man davon profitieren. Die Schlussfolgerung „Wenn man keine Popups verwendet, geht die Accessibility kaputt“ klingt seltsam, aber wenn man ein Audit bestehen muss, kann es nötig sein, diesen Weg zu gehen.
Auch für Fehler, die keinem bestimmten Feld zugeordnet sind, gibt es Techniken. In meinem Projekt verwenden wir eine
HiddenValidationInput-Komponente, die ein unsichtbares, schreibgeschütztes Input erzeugt, um Custom Errors zu rendern, die zu keinem anderen Feld gehören. Man muss sie nur bedingt rendern, daher ist sie in der Praxis ziemlich angenehm zu verwenden.Ich stimme völlig zu, dass die
customValidity-API imperativ und umständlich ist. Genau darüber habe ich einen Artikel geschrieben, und ich hoffe, dass sich auch dieser Teil mit der Zeit verbessert.Chromes grobes und wenig barrierefreies Design ist ein Chrome-Problem, also werde ich einen Bug Report einreichen.
Ist mit „Spezifikation“ das hier gemeint? https://html.spec.whatwg.org/multipage/dom.html#concept-elem...
Ich sehe keinen Abschnitt, der definiert, wie Validierungsmeldungen angezeigt werden müssen. Nicht einmal, dass es überhaupt eine Meldung geben muss.
Ehrlich gesagt wirken die Leute, die solche Spezifikationen schreiben, realitätsfern und so, als würden sie das, was sie spezifiziert haben, selbst nicht wirklich benutzen. Für sehr einfache Dinge funktioniert es, aber sobald Formulare sich weiterentwickeln, merkt man irgendwann, dass man besser alles selbst schreibt.
Sobald man mit JS anfängt, ist es viel einfacher, alles in Code zu machen, als an Validierungsattributen herumzudrehen.
Wenn eine Checkbox ein Label hat, würde ich mir wünschen, dass das Label ein
for-Attribut bekommt, damit man die Checkbox durch Klicken auf das Label ein- und ausschalten kann. Das ist persönlich eines der Dinge, die mich am meisten stören, aber vielleicht bin das nur ich.inputmit einemlabelumschließt. Ich weiß nicht genau, warum Leute dazu neigen, die beiden getrennt zu halten.Und ich weiß auch nicht, warum Browser angefangen haben, diese Dinge zu trennen. Aus meiner Sicht sollte nicht der Text Checkboxen und Radiobuttons enthalten, sondern Checkboxen und Radiobuttons sollten den Text enthalten.
Ein einfaches Beispiel ohne React gibt es hier:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectE...
HTML-Formularvalidierung ist großartig. Es gibt allerdings eine riesige Falle:
In Firefox für Android funktioniert sie nicht.
https://bugzilla.mozilla.org/show_bug.cgi?id=1510450
Es lohnt sich nicht, den Standard wegen weniger als 1 % der Nutzer zu ignorieren, die die Fehlermeldungen bei rot markierten Controls nicht sehen können. Je mehr Websites das verwenden, desto größer wird der Druck auf Mozilla, den Browser zu reparieren.
Das ist keine API wie WebMIDI, die Chrome oder Safari vor der Standardisierung implementiert haben, sondern Teil der ursprünglichen HTML5-Spezifikation.
Besonders, wenn man bedenkt, wie wenig Zeit darauf verwendet wird, sicherzustellen, dass Menschen mit Accessibility-Software Websites richtig nutzen können. Wenn man nicht bereit ist, auch über UC Browser zu sprechen, ist dieses Thema hier meiner Ansicht nach kaum erwähnenswert.
Trotzdem ist es ein starkes Argument, uBlock Origin zusammen mit anderen Erweiterungen nutzen zu können.
[1] https://bugzilla.mozilla.org/show_bug.cgi?id=1884282
[2] https://bugzilla.mozilla.org/show_bug.cgi?id=1897707
Wenn die Validierung gar nicht implementiert wäre, wäre das fast besser; so ist es wirklich ein Chaos. Ich habe das vor ein paar Jahren nach langem, schmerzhaftem Debugging herausgefunden, und ich bin sicher, dass ich weder der Erste noch der Letzte war, dem das passiert ist.
Bevor ich danach gesucht habe, konnte ich mir nicht vorstellen, was ich übersehen hatte, und es war ziemlich schockierend. Eigene Validierung zu bauen ist okay, aber das hier sollte funktionieren.
Man sollte aufpassen, es nicht zu übertreiben.
Kürzlich wollte ich bei Groupon eine Erstattung bekommen. Der Anbieter, bei dem ich den Groupon gekauft hatte, war unter neuer Geschäftsführung und erkannte meinen Groupon nicht an.
Das Formular hatte eine Bedingung „mindestens 15 Wörter“, aber egal was ich tat, ich kam nicht durch die Validierung und musste am Ende das HTML inspizieren.
\wstand für Wortzeichen,\bfür Wortgrenze,\sfür Whitespace, und Satzzeichen waren buchstäblich überhaupt nicht erlaubt.patternsind nett, aber nicht ausreichend.Zum Beispiel lässt sich das Beispiel „Passwort wiederholen“ auch ohne
setCustomValiditymit dem Attributpatternumsetzen. Dafür müsste man dynamisch aus dem ersten Eingabewert einen regulären Ausdruck erzeugen.Ich habe die Lösungen nicht verglichen, weil der Artikel sonst zu lang geworden wäre, aber der Kernpunkt ist: Mit
customValiditywird die Validierung viel klarer und lesbarer. Eine bessere API macht hier einen großen Unterschied.Auch die Einschränkung „mindestens 15 Wörter“ ließe sich viel schöner ausdrücken, etwa als
value.split(/\s+/).length >= 15.Es gibt Gründe, warum sie weniger genutzt wird. Viele Frameworks und Bibliotheken bieten robuste und gut stylbare Validierungsfunktionen, manche davon sehr ausgefeilt und erweiterbar. Wenn man sich die Mühe nicht machen muss, sollte man sich nicht selbst quälen.
curloder einem anderen Tool ohne eingebaute Formularvalidierung an das Formular geht.Man kann nicht darauf vertrauen, dass der Client validiert hat oder es korrekt getan hat; das Backend muss Eingaben weiterhin prüfen und das Formular mit Validierungsfehlern für alle Felder anzeigen können.
Frontend-Validierung ist nur dazu da, Nutzern zu helfen. Aber wenn man sie stylen oder beim Absenden vom Backend aus auslösen will, muss man am Ende doch eigenes Styling implementieren.
Es gibt zu viele Fallstricke, und wenn man komplexere Prüfungen einfach unterstützen will, landet man am Ende doch bei einer Bibliothek.
Außerdem eröffnet eine Bibliothek je nach Fall die Möglichkeit, Teile des Validierungscodes zwischen Frontend und Backend zu teilen.
Besonders dieser Artikel scheint ein Problem mit
useLayoutEffectzu umgehen, und das sollte man nicht leichtfertig tun.Eine Sache, die ich an HTML-Formularvalidierung nicht mag, ist, dass sie ab dem Laden der Seite läuft. Wenn man zum Beispiel Fehlerzustands-Styling daran koppelt, wird das Formular von Anfang an voller Fehler geladen und kann auf Nutzer einschüchternd wirken.
:user-invalid, die das bis zu einem gewissen Grad vermeidet. Sie ist allerdings nicht besonders flexibel und reicht je nach Anwendungsfall möglicherweise nicht aus.https://developer.mozilla.org/en-US/docs/Web/CSS/:user-inval...
Man kann das per Skript umgehen, aber an diesem Punkt leistet die Funktion nicht mehr besonders viel. Ich denke, genau das ist der Hauptgrund, warum sie nicht breiter eingesetzt wird.