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
- Constraint Validation in HTML (Sie sind hier!)
- Die Constraint Validation API in JavaScript
- Ein Validity State API Polyfill
- 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>

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">

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>

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>

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
- Constraint Validation in HTML (Sie sind hier!)
- Die Constraint Validation API in JavaScript
- Ein Validity State API Polyfill
- Validierung des MailChimp-Anmeldeformulars
Tolle Einführung!
Sie planen vielleicht, dies in Ihrem nächsten Beitrag zu behandeln, aber Sie können Chrome zwingen, Felder bei Fokusverlust zu validieren (oder jeden Browser zu zwingen, Felder jederzeit zu validieren) mit der Funktion
checkValidity(): https://developer.mozilla.org/en-US/docs/Web/API/HTMLSelectElement/checkValidityBearbeitung: Es scheint, dass die Unterstützung dafür etwas komplexer ist, als ich dachte. HTMLInputElement hat sie auch, aber HTMLFormElement hat stattdessen
reportValidity(), aus irgendeinem Grund. Ich bin mir bei anderen Formularfeldelementen nicht sicher.Im nächsten Artikel spreche ich über Validity State, was dasselbe tut wie
checkValidity(), aber mit mehr/besseren Funktionen.Wir werden einen JavaScript-gestützten Validator rund um unsere nativen Constraint Validation Attribute erstellen, ohne eine riesige Bibliothek mit vielen Regex-Mustern schreiben zu müssen.
Und um Ihre Bedenken hinsichtlich der Unterstützung anzusprechen: Im dritten Artikel erstellen wir ein leichtgewichtiges Polyfill, um fehlende Funktionen für alle Browser nachzurüsten und die Unterstützung bis IE9 zurückzuverfolgen.
Schön, eine umfassende Zusammenfassung dieser oft übersehenen Ecke der Frontend-Entwicklung zu sehen. Ich muss fast immer ein Formular auf einer Website implementieren, und meine Validierungsmethoden ändern sich ständig. Es ist schön, einen zentralen Ort mit viel Überblick über gut getestete Methoden zu sehen.
Ich liebe diesen Artikel! Als jemand, der sich intensiv mit Formularvalidierungen und Eingabemaskierung beschäftigt und versucht, die JS-Last gering zu halten, ist es großartig, eine gründliche Aufstellung der nativen HTML5-Funktionen zu sehen. Ich habe eine Beobachtung zu diesem Abschnitt
Ich möchte darauf hinweisen, dass MS Edge ebenfalls einen großartigen nativen Datums-Picker für
input type="date"hat.Mein Mac-Bias zeigt sich hier. Ich habe den Artikel gerade aktualisiert. Danke für den Hinweis!
Das Warten auf Firefox hat bald ein Ende, zumindest: Date/Time Inputs Enabled on Nightly
Danke! Ich hatte davon gehört, konnte aber zum Zeitpunkt des Schreibens keinen Link zur Bestätigung finden. Habe den Artikel entsprechend aktualisiert.
Toller Artikel, aber glaubst du nicht, dass nicht-native Formularvalidierung für flexiblere Lösungen verwendet wird? Zum Beispiel ist es unmöglich, Fehlermeldungen anders als auf Englisch zu haben.
Ich stimme vollkommen zu! Ich denke, die Constraint Validation Attribute allein reichen nicht aus, und das von Ihnen angesprochene Problem – nicht-englische Fehlermeldungen – ist ein wichtiges, das ich nicht erwähnt habe (und hätte sollen).
Morgen in Teil 2 werde ich durchgehen, wie wir eine native JavaScript-API verwenden können, um ein super leichtgewichtiges benutzerdefiniertes Validierungsskript zu erstellen, das Ihnen die Flexibilität gibt, Fehler anzupassen, zu ändern, wie und wann sie angezeigt werden, und mehr.
Ich würde es immer noch als "nativ" betrachten, aber es ist robuster als die reine HTML-Version und *viel* kleiner als das, was man oft bekommt, wenn man ein komplett nicht-natives Plugin verwendet.
Eine Sache, die ich immer interessant finde, ist, dass minlength umgangen werden kann, wenn der Benutzer Multibyte-Zeichen (wie Emojis) verwendet. minlength und maxlength beziehen sich tatsächlich auf die Anzahl der Bytes und nicht auf die Anzahl der Zeichen, daher hat "☹️" eine Länge von 2.
Dies könnte auch Probleme für Sprachen verursachen, die regelmäßig Multibyte-Zeichen verwenden.
Schöner Artikel. Eine kleine Ergänzung zum Bonus-CSS-Tipp.
Um ungültige Selektoren nur dann zu stylen, wenn sie nicht fokussiert und leer sind (aber einen Platzhalter haben), können Sie Folgendes tun:
Hinweis: IE und Edge unterstützen
:placeholder-shownnicht„[…] 28-mal (2.800 %) weniger Code.“
So funktioniert Mathematik nicht. Wenn der andere Code 28-mal so viel ist wie Ihrer, ist Ihr Code 1/28-tel oder 3,6 % des anderen Codes. Das sind 96,4 % weniger.
2800 % weniger würde bedeuten: Wenn die andere Codebasis 100 Zeilen hat, hat Ihre -2700.
</Schlaumeiermodus aus>
Danke, Dominic! Ich habe wirklich mit der Berechnung des Prozentsatzes gerungen und offensichtlich versagt. Ich habe das Prozentzeichen gerade entfernt.
Gute Zusammenfassung, Chris.
Werden Sie in Ihrem nächsten Artikel ARIA-Unterstützung über JavaScript hinzufügen?
In Teil 2 berühre ich, wie man Fehlermeldungen mit ihren entsprechenden Feldern über
aria-describedbyverknüpft. Aber tiefer gehe ich nicht.Das Muster für Zahlen zwischen 3 und 42 ist falsch:
[3-42]stimmt nur mit "2", "3" oder "4" überein.Sie müssten stattdessen etwas wie
([3-9]|[1-3][0-9]|4[0-2])verwenden.Danke! Offensichtlich ist die dunkle Kunst der Regex nicht meine Stärke. Ich schätze das sehr. Habe den Beitrag gerade aktualisiert.
Gut. Es sieht so aus, als ob die Muster für TLDs nur 2..3 Zeichen zulassen, während sie länger sein könnten: Laut RFC 1034 kann die TLD-Länge bis zu 63 Oktette betragen.
Guter Fang, Paul! Das habe ich beim Schreiben der Serie bemerkt und dachte, ich hätte es überall korrigiert, aber ein oder zwei Stellen übersehen. Korrigiert!
Bei Datumsfeldern wird das angezeigte Format aus den Einstellungen des Betriebssystems des Benutzers übernommen. Es gibt keine Garantie, dass sie im US-Format MM/TT/JJJJ sind.
Zum Beispiel habe ich auf meinem Computer Daten im Format JJJJ-MM-TT formatiert, und so werden sie im Feld in Chrome angezeigt.
Vielen Dank für die Klärung. Ich habe den Beitrag entsprechend aktualisiert.
Wie macht man dann i18n-Lokalisierung?
Es sieht so aus, als ob sie automatisch lokalisiert werden über die Browsersprache des Benutzers, was möglicherweise nicht die genaueste Methode ist.
In Teil 2 stelle ich eine JavaScript-Lösung bereit, mit der Sie sowohl den Ort als auch den Text der Nachrichten anpassen können, was für Sie möglicherweise eine bessere Option ist.
Ich habe das Gefühl, ich habe jahrelang auf diesen Artikel gewartet, also danke, Chris.
Bezüglich der Einschränkung, keine benutzerdefinierten Fehlermeldungen anzuzeigen, könnten die Meldungen nicht angezeigt werden, indem die Fehlermeldung in HTML eingefügt und mit
Danke, Luca!
PPK geht tatsächlich auf diese Art von Dingen in seiner Serie über native Formularvalidierung ein und identifiziert einige potenzielle Herausforderungen damit.
PPK kommt letztendlich zu einem anderen Schluss als ich über den Gesamtzustand der Dinge
Ich stimme ihm nicht zu, aber wenn Sie einen *wirklich* detaillierten Einblick in die Funktionsweise aller Dinge unter der Haube wünschen, ist seine Serie es wert.