Formularze - walidacja

Sprawdzanie danych, które wprowadza użytkownik to jedna z ważniejszych rzeczy o jaką musimy zadbać, ale równocześnie też nie zawsze prosta do wykonania. Dane możemy sprawdzać na wiele, wiele sposobów, które często będzie uzależnione od budowy formularza i zawartych w nim mechanizmów.

Kilka informacji na temat struktury HTML formularza

Budowa formularza

Zanim przejdziemy do walidacji, zajmijmy się na chwilę kodem naszego formularza.


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

Powyższy kod nie jest żadnym wyznacznikiem jakości. Formularze można budować na wiele sposób, a które dość często nie będą od nas zależeć, bo dla przykładu będą budowane przez jakieś pluginy (dość częsta praktyka w takim Wordpressie).

Jeżeli jednak tworzysz taki kod samemu, warto byś zwrócił uwagę na kilka rzeczy.

Po pierwsze każde pole powinno mieć swój label. Dzięki temu zwiększamy dostępność takich pól. Nawet jeżeli na layoucie takich labeli nie ma, nie znaczy to, że nie powinny znaleźć się w kodzie (można je ukryć dowolną techniką CSS). Żeby oba elementy - input i jego label - stanowiły zamkniętą całość, okryłem je dodatkowymi divami .form-row. Dzięki temu o wiele prościej będzie nam nadawać odstępy między elementami. Dodatkowo można by także pokusić się o dodanie dodatkowych klas dla labeli (.form-label) i samych inputów (.form-control), dzięki czemu w przyszłości nasze stylowanie było by jeszcze prostsze. W poniższych kodach tego unikam by za bardzo nie zaśmiecać kodu.

Dość często widziałem, jak niektórzy robili formularze w strukturze o wiele prostszej:


    <form class="form" method="post" action="...gdzie-wysylamy...">
        <label for="name">Imię (min. 3 znaki)*</label>
        <input type="text" name="name" id="name">

        <label for="email">Email*</label>
        <input type="email" name="email" id="email">

        <button type="submit" class="button submit-btn">
            Wyślij
        </button>
    </form>
    

Unikają dzięki temu zbędnego kodu, ale równocześnie tracą możliwości. Jak zobaczysz później, do każdego pola będziemy chcieli dodać dodatkowe opisy z błędami.

W strukturze takiej jak powyżej było by to dla nas bardzo problematyczne do stylowania. Jeżeli mamy dla każdego inputa rodzica w postaci .form-row, taki błąd możemy stylować w dowolny sposób. Może to być prosty tekst pod polem (jak w artykule poniżej), ale i spokojnie możemy z niego zrobić mała ikonkę pozycjonowaną absolutnie względem .form-row. A nawet jak będziemy chcieli do naszego formularza dodać nowy element z informacjami o wysyłce, nie będzie z tym żadnego problemu. Ogólnie więc nie warto oszczędzać na kilku dodatkowych divach.

Jak przeprowadzić walidację?

  1. Pierwszy etap to dynamiczna podpowiedź w czasie wprowadzania danych przez użytkownika. Wykorzystamy tutaj zdarzenia change, focus, blur, keyup, keydown, keypress lub input - wszystko zależnie od sytuacji. Dodanie takich dynamicznych podpowiedzi znacząco może poprawić użyteczność naszego formularza, ale nie zawsze jest stosowane. Niektóre z takich testów robiliśmy w poprzednim rozdziale.
  2. Drugi etap to sprawdzenie danych tuż przed wysłaniem. Jeżeli są poprawne to je prześlemy na serwer. Jeżeli nie, wyświetlamy stosowną informację (ewentualnie wskazujemy błędne pola) i blokujemy wysyłkę.
  3. Ostatni - najważniejszy etap - to sprawdzenie przesłanych danych po stronie serwera. Jeżeli dane są błędne, wówczas wracamy do formularza wyświetlając informację i czekamy na wykonanie punktu drugiego.

I nie - nie jest to jedyny przepis na sprawdzanie formularzy.

W idealnym świecie do powyższych kroków doszedł by jeszcze krok 0. Od kilku lat w przeglądarkach istnieje bowiem walidacja po stronie HTML. Jeżeli dobrze zbudujemy nasz formularz (dodając do niego odpowiednie atrybuty, stosując odpowiednie typy pól itp) wtedy możemy mieć podstawową walidację bez napisania nawet kawałka Javascript.

Jest to fajna sprawa, ponieważ idealnie wkomponowuje się w progressive enhancement. Jeżeli na stronę użytkownik wejdzie bez Javascript, dostanie podstawową walidację, która może nie jest idealna, ale jest. Na to my jako programiści robimy nakładkę w postaci naszej walidacji Javascript, która - znowu - w idealnym świecie - przeprowadziła by użytkownika przez wszystkie powyższe kroki. Jest to też o tyle fajne, ponieważ w Javascript mamy specjalny interfejs do sprawdzania formularzy (porozmawiamy o nim w kolejnym rozdziale), który swoje działanie opiera na walidacji HTML. Jeżeli więc dobrze stworzymy nasz początkowy kod HTML, nasze zadanie będzie o wiele prostsze.

Problem jest niestety taki, że nie żyjemy w idealnym świecie. Dość często formularze które musimy obsłużyć są tak zaawansowanymi tworami, że standardowa HTMLowa walidacja nie wystarczy.

Walidacji za pomocą Javascript raczej nie unikniemy.

Tak naprawdę ciężko mówić o pełnoprawnej walidacji danych po stronie przeglądarki (pierwszy i drugi krok). Możemy dane sprawdzać i pokazywać użytkownikowi podpowiedzi, ale opieranie się w całości na walidacji danych tylko po stronie przeglądarki jest mocno naiwne. Spreparowanie odpowiedniego formularza, czy nagięcie działania skryptów nie jest zbyt ciężką rzeczą. Szczególnie jeżeli mamy dostęp do narzędzi developerskich (1, 2).

Prawdziwa walidacja danych powinna zawsze się odbywać po stronie serwera. Skrypty w przeglądarce traktuj tylko jako poprawę użyteczności formularza i podpowiedzi dla użytkownika. Nic więcej.

Prosta walidacja

Zacznijmy od podstaw. Domyślną akcją każdego formularza jest wysłanie danych, co powoduje przeładowanie danej strony, lub przeniesienie na inną. Aby wykonać jakąkolwiek walidację, musimy tą akcję przerwać.

Najlepszym sposobem na to jest podpięcie się pod zdarzenie submit formularza, a następnie użycie preventDefault(), po którym możemy w spokoju przeprowadzać sprawdzanie pól:


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

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

    //jeżeli wszystko ok to wysyłamy
    if (input.value.length >= 3) {
        form.submit();
    } else {
        //jeżeli nie to pokazujemy jakieś błędy
        alert("Kolego wypełniłeś błędnie nasz super formularz");
    }
})

Niektórzy zamiast pod zdarzenie submit podpinają się pod zdarzenie click dla przycisków Submit. I takie działanie także zadziała, nawet gdy użytkownik wyśle formularz enterem. W moim odczuciu jest to jednak mało eleganckie rozwiązanie.

Ważniejsze jest jednak coś innego. Gdy formularz wysyła użytkownik np. klikając w przycisk Wyślij, wpierw odpalana jest HTMLowa walidacja (którą możemy wyłączyć atrybutem novalidate dla formularza), a następnie zdarzenie "submit", do którego się powyżej podpięliśmy.
Wywołanie metody form.submit() powoduje wysłanie formularza, jednak ani HTMLowa walidacja, ani zdarzenie "submit" nie zostanie aktywowane. Możemy to obejść na trzy sposoby. Po pierwsze zamiast metody form.submit() możemy za pomocą kodu kliknąć w przycisk submit w danym formularzu np. button.click(). Drugim sposobem może być stworzenie własnego zdarzenia "submit" (za pomocą new Event()) , które poślemy do danego formularza:


        const event = new Event("submit");
        form.dispatchEvent(event);
    

Od jakiegoś czasu mamy też dostęp do metody form.requestSubmit(), która zarówno aktywuje HTMLową walidację jak i uruchamia zdarzenie "submit". Trzeba mieć tylko na uwadze, że nie zadziała w starszych przeglądarkach (IE11).

Gdy pojawi się w naszym formularzu kilka pól, trzeba jakoś zebrać wyniki. Rozwiązań jest wiele. Jednym z nich jest ręczne sprawdzanie kolejnych pól, gdzie wyniki testów możemy trzymać w oddzielnej tablicy.

Równocześnie posługiwanie się okienkiem alert nie jest zalecanym rozwiązaniem. Okienko takie jest zbyt "intensywne" dla użytkownika końcowego, a poza tym nie mamy w ogóle możliwości zmiany jego wyglądu. Przyda się w prostych treningowych rozwiązaniach, ale my chcemy więcej. Pokażmy błędy jako informacja wkomponowana w formularz.


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

const form = document.querySelector("form");
const inputName = form.querySelector("input[name=name]");
const inputEmail = form.querySelector("input[name=email]");
const formMessage = form.querySelector(".form-message");

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

    let formErrors = [];

    //-------------------------
    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    //-------------------------
    if (inputName.value.length <= 3) {
        formErrors.push("Wypełnij poprawnie pole z imieniem");
    }

    //wyrażenie testujące maila omawiane w rozdziale o wyrażeniach regularnych
    const reg = /^([^\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,})+$/;
    if (!reg.test(inputEmail.value)) {
        formErrors.push("Wypełnij poprawnie pole z emailem");
    }

    if (!formErrors.length) { //jeżeli nie ma błędów wysyłamy formularz
        e.target.submit();
        //...lub dynamicznie wysyłamy dane za pomocą Ajax
        //równocześnie reagując na odpowiedź z serwera
    } else {
        //jeżeli jednak są jakieś błędy...
        formMessage.innerHTML = `
            <h3 class="form-error-title">Przed wysłaniem proszę poprawić błędy:</h3>
            <ul class="form-error-list">
                ${formErrors.map(el => `<li>${el}</li>`).join("")}
            </ul>
        `;
    }
});

W powyższym kodzie pominęliśmy etap pierwszy, czyli podpowiedzi dla użytkownika w trakcie pisania.

Żeby nie powtarzać warunków testów stwórzmy oddzielne funkcje do testowania i dodawania klasy:


function testText(field, lng) {
    return field.value.length >= lng;
}

function testEmail(field) {
    const reg = /^([^\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,})+$/;
    return reg.test(field.value);
}

function markFieldAsError(field, hasError) {
    field.classList.toggle("field-error", hasError);
}

A następnie wykorzystajmy je w naszym kodzie podpinając je pod zdarzenia input lub blur po wysyłce formularza.


function testText(field, lng) {
    ...
}

function testEmail(field) {
    ...
}

function markFieldAsError(field, hasError) {
    ...
}

//------------------------
//pobieram elementy
//------------------------
const form = document.querySelector("form");
const inputName = form.querySelector("input[name=name]");
const inputEmail = form.querySelector("input[name=email]");
const formMessage = form.querySelector(".form-message");


//------------------------
//etap 1 : podpinam zdarzenia
//------------------------
inputName.addEventListener("input", e => markFieldAsError(e.target, !testText(e.target, 3)));
inputEmail.addEventListener("input", e => markFieldAsError(e.target, !testEmail(e.target)));

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

    let formErrors = [];

    //------------------------
    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    //------------------------
    //chowam błędy
    for (const el of [inputName, inputEmail]) {
        markFieldAsError(el, false);
    }

    //i testuję w razie czego zaznaczając pola
    if (!testText(inputName, 3)) {
        markFieldAsError(inputName, true);
        formErrors.push("Wypełnij poprawnie pole z imieniem");
    }

    if (!testEmail(inputEmail)) {
        markFieldAsError(inputEmail, true);
        formErrors.push("Wypełnij poprawnie pole z emailem");
    }

    if (!formErrors.length) { //jeżeli nie ma błędów wysyłamy formularz
        form.submit();
        //...lub dynamicznie wysyłamy dane za pomocą Ajax
        //równocześnie reagując na odpowiedź z serwera
    } else {
        //jeżeli jednak są jakieś błędy...
        formMessage.innerHTML = `
            <h3 class="form-error-title">Przed wysłaniem formularza proszę poprawić błędy:</h3>
            <ul class="form-error-list">
                ${formErrors.map(el => `<li>${el}</li>`).join("")}
            </ul>
        `;
    }
});

Błędy przy polach

Możemy też pokusić się o pokazywanie błędów przy błędnych polach. Rozwiązań jest tutaj kilka - lepszych i gorszych - a na pewno różnych.

Najprostsze z nich może polegać na ręcznym wstawieniu komunikatów do HTML za danym polem.


...
<div class="form-row">
    <label for="name2A">Imię (min. 3 znaki)*</label>
    <input type="text" name="name" id="name">
    <div class="form-error-text">Wpisz poprawne imię</div>
</div>
...

Domyślnie takie komunikaty powinny by ukryte (display: none), a pokazywalibyśmy je w momencie, gdy dane pole będzie błędnie wypełnione.


//funkcje testujące
function testText(field, lng) {
    ...
}

function testEmail(field) {
    ...
}

function toggleErrorField(field, show) {
    const errorText = field.nextElementSibling;
    if (errorText !== null) {
        if (errorText.classList.contains("form-error-text")) {
            errorText.style.display = show ? "block" : "none";
        }
    }
}

function markFieldAsError(field, hasError) {
    if (hasError) {
        field.classList.add("field-error");
    } else {
        field.classList.remove("field-error");
        toggleErrorField(field, false);
    }
}

//pobieram elementy
const form = document.querySelector("form");
const inputName = form.querySelector("input[name=name]");
const inputEmail = form.querySelector("input[name=email]");
const formMessage = form.querySelector(".form-message");

//etap 1 : podpinam zdarzenia
inputName.addEventListener("input", e => markFieldAsError(e.target, !testText(e.target, 3)));
inputEmail.addEventListener("input", e => markFieldAsError(e.target, !testEmail(e.target)));

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

    let formErrors = false;

    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    //chowam błędy by zaraz w razie czego je pokazać
    for (const el of [inputName, inputEmail]) {
        markFieldAsError(el, false);
        toggleErrorField(el, false);
    }

    if (!testText(inputName, 5)) {
        markFieldAsError(inputName, true);
        toggleErrorField(inputName, true);
        formErrors = true;
    }

    if (!testEmail(inputEmail)) {
        markFieldAsError(inputEmail, true);
        toggleErrorField(inputEmail, true);
        formErrors = true;
    }

    if (!formErrors) {
        e.target.submit();
    }
});
Wpisz poprawne imię
Wpisz poprawny email

Dynamicznie tworzone komunikaty

Jeżeli nie mamy wpływu na to jaki będzie HTML, komunikaty o błędach możemy też robić za pomocą samego Javascript. Domyślnie nie będzie ich w HTML, a będziemy je do niego wstawiać dopiero gdy pokażemy dany błąd.


function removeFieldError(field) {
    const errorText = field.nextElementSibling;
    if (errorText !== null) {
        if (errorText.classList.contains("form-error-text")) {
            errorText.remove();
        }
    }
};

function createFieldError(field, text) {
    removeFieldError(field); //przed stworzeniem usuwam by zawsze był najnowszy komunikat

    const div = document.createElement("div");
    div.classList.add("form-error-text");
    div.innerText = text;
    if (field.nextElementSibling === null) {
        field.parentElement.appendChild(div);
    } else {
        if (!field.nextElementSibling.classList.contains("form-error-text")) {
            field.parentElement.insertBefore(div, field.nextElementSibling);
        }
    }
};

Dodatkowo lekko przerobimy wcześniejszy kod:


//funkcje testujące
function testText(field, lng) {
    ...
}

function testEmail(field) {
    ...
}

function removeFieldError(field) {
    ...
}

function createFieldError(field, text) {
    ...
}

function markFieldAsError(field, hasError) {
    if (hasError) {
        field.classList.add("field-error");
    } else {
        field.classList.remove("field-error");
        removeFieldError(field);
    }
}

//pobieram elementy
const form = document.querySelector("form");
const inputName = form.querySelector("input[name=name]");
const inputEmail = form.querySelector("input[name=email]");
const formMessage = form.querySelector(".form-message");

//etap 1 : podpinam eventy
inputName.addEventListener("input", e => markFieldAsError(e.target, !testText(e.target, 3)));
inputEmail.addEventListener("input", e => markFieldAsError(e.target, !testEmail(e.target)));

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

    let formErrors = false;

    //2 etap - sprawdzamy poszczególne pola gdy ktoś chce wysłać formularz
    //wpierw usuwamy błędy
    for (const el of [inputName, inputEmail]) {
        markFieldAsError(el, false);
        removeFieldError(el);
    }

    //następnie sprawdzamy każde pole
    if (!testText(inputName, 3)) {
        markFieldAsError(inputName, true);
        createFieldError(inputName, "Wpisana wartość jest niepoprawna");
        formErrors = true;
    }

    if (!testEmail(inputEmail)) {
        markFieldAsError(inputEmail, true);
        createFieldError(inputEmail, "Wpisany email jest niepoprawny");
        formErrors = true;
    }

    if (!formErrors) {
        e.target.submit();
    }
});

Opcjonalnie moglibyśmy jeszcze tutaj nieco usprawnić powyższą walidację.

W tym momencie pole robi się czerwone od razu gdy użytkownik zacznie wpisywać. Według badań przeprowadzonych przez Christiana Holsta i Luka Wroblewskiego najlepszym sposobem informowania użytkownika o błędach jest moment, kiedy opuszcza on dane pole. Faktycznie coś w tym jest. My zaczynamy wpisywać, a od razu mamy błąd...
Z drugiej strony gdy użytkownik dostanie już błąd, wróci do pola i zacznie je poprawiać, fajnie by było gdyby widział na bieżąco, że wpisana wartość jest już poprawna. Ok - zróbmy to.

Linie 35-36 zamieniamy na:


    ...

    inputName.addEventListener("blur", e => {
        const testResult = !testText(e.target, 3) && e.target.value !== "";
        markFieldAsError(e.target, testResult);
    });

    inputName.addEventListener("input", e => {
        if (e.target.classList.contains("field-error") && testText(e.target, 3)) {
             markFieldAsError(e.target, false);
        }
    });

    inputEmail.addEventListener("blur", e => {
        const testResult = !testEmail(e.target) && e.target.value !== "";
        markFieldAsError(e.target, testResult);
    });
            
    inputEmail.addEventListener("input", e => {
        if (e.target.classList.contains("field-error") && testEmail(e.target)) {
             markFieldAsError(e.target, false);
        }
    });

    ...
    

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">Imię*</label>
        <input type="text" name="name" id="name" pattern=".{3,}" required>
    </div>
    <div class="form-row">
        <label for="email">Email*</label>
        <input type="email" name="email" id="email" required>
    </div>
    <div class="form-row">
        <label for="message">Wiadomość*</label>
        <textarea name="message" id="message" required></textarea>
    </div>
    <div class="form-row">
        <button type="submit" class="submit-btn">
            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

Za pomocą Javascript możemy zmienić treść takiego dymka, ale wyglądu nie zmienimy.

Dla pola Imię zastosowałem dodatkowy atrybut pattern .{3,}, który oznacza minimum 3 znaki dowolnego typu. Atrybut required oznacza, że dane pole musi być wypełnione, co dla pól tekstowych oznacza, że trzeba cokolwiek do takiego pola wpisać.

Jeżeli chcemy zmienić ten warunek, musimy używać atrybutu pattern (4 linia), w którym podajemy wyrażenie regularne.

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

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 (czyli 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ł "czerwone 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=" ">
    

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.