Formularze - Constraint validation API

Walidacja w czystym HTML

Od kilku lat w HTML5 dostępna jest wbudowana walidacja, która swoje działanie opiera o zastosowanie odpowiednich typów pól formularza i atrybutów dla nich.

Jest to bardzo ciekawe pomysł, który pozwala wprowadzać walidację bez pisania kawałka kodu. Dzięki odpowiedniemu stworzeniu kodu formularza tylko zyskujemy. Dostajemy "darmową" walidację, która działa nawet, gdy użytkownik ma wyłączony Javascript. Po drugie dzięki zastosowaniu odpowiednich typów pól (np. type="email" dla pola z emailem, type="number" dla liczb) zyskujemy dodatkowe poprawienie użyteczności naszego formularza, bo np. na urządzeniach mobilnych wyświetlane są odpowiednie klawiatury. Same plusy.

Listę dostępnych typów pól najlepiej zobaczyć na stronie https://developer.mozilla.org/pl/docs/Web/HTML/Element/Input. Warto mieć jednak na uwadze, że nowe rodzaje pól nie w każdej przeglądarce będą się wyświetlać tak samo.

Jeżeli chodzi o atrybuty, to mamy kilka do wykorzystania:

required określa czy dane pole ma być wypełnione
minlength, maxlength atrybuty określające minimalna i maksymalną długość wpisywanego tekstu
min, max określa minimalną i maksymalną liczbę w polach numerycznych lub dla pól range
type określa typ pola. Niektóre z pól mają swoją własną domyślną walidację. I tak np. pola typu email wymagają wpisania emaila, a pola url wpisania odpowiedniego adresu url
pattern pozwalają nam podać własny wzór (w formacie regexp) który będzie używany do testu poprawności pola

Spróbujmy to wykorzystać z prostym formularzu kontaktowym:


<form class="form" id="formTest1" method="post">
    <div class="form-row">
        <label for="name" class="form-label">Imię*</label>
        <input type="text" class="form-control" name="name" id="name" pattern=".{3,}" required>
    </div>
    <div class="form-row">
        <label for="email" class="form-label">Email*</label>
        <input type="email" class="form-control" name="email" id="email" required>
    </div>
    <div class="form-row">
        <label for="message" class="form-label">Wiadomość*</label>
        <textarea class="form-control" name="message" id="message" required></textarea>
    </div>
    <div class="form-row">
        <button type="submit" class="button form-submit">
            Wyślij
        </button>
    </div>
</form>

Spróbuj teraz wysłać taki formularz. Zobaczysz, że przeglądarka sama w sobie będzie pokazywać odpowiedni komunikat z błędem danego pola.

Walidacja HTML

Dymki z podpowiedzią pokazują się dopiero w drugim kroku, czyli przy próbie wysłania formularza. Jeżeli chcielibyśmy do naszego formularza dodać dynamiczną podpowiedź w czasie wpisywania, możemy to zrobić za pomocą CSS oraz odpowiedni pseudoklas:

:valid czy pole jest poprawnie wypełnione
:invalid czy pole jest źle wypełnione
:user-valid czy pole jest poprawnie wypełnione, ale po pierwszej interakcji użytkownika z polem
:user-invalid czy pole jest źle wypełnione, ale po pierwszej interakcji użytkownika z polem

np:


input:user-invalid {
    border-color: red;
    outline: none;
    box-shadow: 0 0 2px red;
}

input:user-valid {
    border-color: #4dcc23;
    outline: none;
    box-shadow: 0 0 2px #4dcc23;
}

<input type="text" name="numbers" required pattern="[0-9]{3,}">

W powyższym kodzie zastosowałem selektory :user-valid i :user-invalid (https://developer.chrome.com/blog/css-wrapped-2023#user-states). Jak zauważysz, ich działanie rozpoczyna się w momencie, gdy użytkownik opuści wypełniane pole (zdarzenie :blur). Są to nowe selektory, dlatego zastanów się, czy aby na pewno będą dobrym wyborem dla twojego projektu (jakie przeglądarki wspiera twój projekt).

Do tej pory przy takiej walidacji mogliśmy korzystać z selektorów :valid i :invalid. Pokazywały one błędy jeszcze przed interakcją użytkownika z polem. Oznaczało to, że użytkownik po wejściu na stronę od razu widział błędnie wypełnione pola. Mogliśmy do obejść np. poprzez dodanie do pola placeholdera np. ze spacją i użycia selektora :placeholder-shown:


    input:invalid { background: red; }
    input::placeholder-shown { background: white; }
    

    <input type="text" name="name" required pattern=".{3,}" placeholder=" ">
    

Constrains Validation API

Javascript udostępnia nam specjalny interfejs - zwany Constrains Validation API, który bardzo ułatwia walidację formularzy, a który sprzężony jest z powyższym mechanizmem z HTML.

Udostępnia on nam kilka metod i właściwości:

Metody:

input.checkValidity() sprawdza czy pole jest poprawnie wypełnione. Zwraca true/false, a jeżeli pole jest błędnie wypełnione, odpala dodatkowo event invalid.
form.reportValidity() Wykonuje sprawdzenie takie jak przy próbie wysłania formularza, który ma walidację w HTML, po czym zwraca true/false. Dla użytkownika oznacza to, że przy pierwszym błędnie wypełnionym polu pojawi się domyślny dymek z komunikatem błędu.
input.setCustomValidity(message) Pozwala ustawić własny komunikat o błędzie, który pojawia się przy walidacji danego pola

Właściwości:

validity zawiera obiekt ValidityState, który jest zbiorem właściwości na temat stanu walidacji danego pola (opisane poniżej)
validationMessage Jeżeli pole podlega walidacji (posiada atrybut required), właściwość ta zawiera aktualną wiadomość, która pojawi się w dymku podczas walidacji. Dla niektórych pól (email, url) wiadomość taka może się zmienić podczas pisania
willValidate Zwraca true/false w zależności od tego, czy pole będzie sprawdzane

Własna walidacja

Powyżej w czystym HTML za pomocą atrybutów i pól dodaliśmy do naszego formularza walidację.

Ten sam mechanizm możemy wykorzystać w Javascript, dzięki czemu unikniemy pisania dodatkowych testów dla danych pól.

Zanim jednak to zrobimy, powinniśmy wyłączyć domyślne HTMLowe dymki poprzez dodanie do formularza atrybutu novalidate:


const form = document.querySelector("form");
form.setAttribute("novalidate", true);

Robimy to specjalnie za pomocą Javascript. Dzięki temu osoba z wyłączonym Javascript dostanie domyślną "brzydką" walidację. Jeżeli Javascript zadziała, wtedy użytkownik będzie mógł się cieszyć jej lepszą wersją.

Jeżeli teraz będziemy chcieli sprawdzać pola, które mają nadane odpowiednie atrybuty (required, pattern, type), możemy wykorzystać metodę checkValidity(), która zwraca true/false w zależności od tego, czy dane pole jest wypełnione poprawnie:


<form class="form" method="post">
    <div class="form-row">
        <label for="name">Imię (min. 3 znaki)*</label>
        <input type="text" required name="name" id="name">
    </div>
    <div class="form-row">
        <label for="email">Email*</label>
        <input type="email" required name="email" id="email">
    </div>
    <div class="form-row">
        <button type="submit" class="button submit-btn">
            Wyślij
        </button>
    </div>
</form>

const form = document.querySelector("form");
const fieldsRequired = form.querySelectorAll("[required]"); //pobieram pola wymagane

//wyłączamy domyślną walidację by nie wyskakiwały nam przeglądarkowe komunikaty
form.setAttribute("novalidate", true);

//dynamiczne pokazywanie błędów w czasie pisania
for (let field of fieldsRequired) {
    field.addEventListener("input", () => {
        field.classList.toggle("is-invalid", field.checkValidity());
    });
}

form.addEventListener("submit", e => {
    e.preventDefault();

    let formHasError = false;

    for (let field of fieldsRequired) {
        if (!field.checkValidity()) {
            field.classList.add("is-invalid");
            formHasError = true;
        } else {
            field.classList.remove("is-invalid");
        }
    }

    //jeżeli nie ma błędów wysyłamy formularz
    if (!formHasError) {
        form.submit();
    }
});

Validity State

Każde z pól formularza ma właściwość validity, która wskazuje na obiekt z kilkoma cennymi właściwościami na temat stanu wypełnienia pola:

valid Zwraca true/false w zależności czy pole przeszło walidację
valueMissing zwraca true gdy pole wymaga jakiejś wartości, a jej nie podano
typeMismatch Zwraca true gdy pole jest typu url/email a podana wartość jest błędna (np. do pola email wpisano wartość, która nie jest emailem, a do url wpisano treść nie będącą adresem)
tooShort Zwraca true jeżeli wpisany tekst jest krótszy niż wartość atrybutu minLength
tooLong Zwraca true jeżeli wpisany tekst jest dłuższy niż wartość atrybutu maxLength
patternMismatch Zwraca true jeżeli pole posiada atrybut pattern, a wpisana wartość nie pasuje do niego
badInput Zwraca true jeżeli wpisana wartość nie może być skonwertowana na wymagany format. Na przykład do pola number wpiszemy tekst nie będący liczbą
stepMismatch Zwraca true jeżeli pole ma atrybut step, a wartość pola nie pasuje do tego skoku
rangeOverflow Zwraca true jeżeli pole posiada atrybut max, a podana wartość go przekroczyła
rangeUnderflow Zwraca true jeżeli pole posiada atrybut min, a podana wartość jest od niego mniejsza

const form = document.querySelector("form");
const input = form.querySelector("input");

form.setAttribute("novalidate", true);

form.addEventListener("submit", e => {
    e.preventDefault();
    console.log(input.validity);
});

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem.
Aha - i ta strona korzysta z ciasteczek.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.