Uwaga! Trwają prace nad nową wersją serwisu. Mogą występować przejściowe problemy z jego funkcjonowaniem. Przepraszam za niedogodności!
🏆 Lubisz podcast Pierwsze kroki w IT? Oddaj swój głos w rankingu: TOP 10 polskich podcastów o IT! 🏆
Poziom podstawowy: jak usprawnić weryfikację pól formularza i wyświetlać komunikaty o błędach? Zrezygnuj z alertów i console.log(), czas przejść na wyższy level. Gdy zrozumiesz już, jak gromadzić komunikaty o błędach w tablicach i pokazywać je w DOM-ie, czeka Cię kolejny artykuł podlinkowany na końcu – o ograniczeniu liczby instrukcji warunkowych. Powodzenia!
W tym przykładzie posłużymy się prostym formularzem do wpisania danych adresowych. Jego kod wygląda w ten sposób:
<!-- novalidate – wyłączam sprawdzanie poprawności danych przez przglądarkę, aby swobodnie testować własne rozwiązanie --> <form action="" method="post" novalidate> <div> <label> Imię <input name="firstName" pattern="^[a-zA-Z –-]+$" required /> </label> </div> <div> <label> Nazwisko <input name="lastName" pattern="^[a-zA-Z –-]+$" required /> </label> </div> <div> <label> Ulica <input name="street" required /> </label> <label> Numer budunku <input name="houseNumber" type="number" required /> </label> <label> Numer mieszkania <input name="flatNumber" type="number" /> </label> </div> <div> <label> Kod pocztowy <input name="zip" pattern="^[0-9]{2}-[0-9]{3}$" required /> </label> <label> Miejscowość <input name="city" pattern="^[a-zA-Z –-]+$" required /> </label> </div> <div><button type="submit">Wyślij</button></div> </form>
Cały kod przedstawionego w tym artykule rozwiązania znajdziesz w repozytorium na GitHubie.
Komunikaty dotyczące formularza moglibyśmy wyświetlać użytkownikowi jako alerty, np. tak:
const formEl = document.querySelector('form'); // sprawdzam, czy formularz został wyszukany i dopiero przypisuję nasłuchiwanie zdarzenia submit if (formEl) { formEl.addEventListener('submit', handleSubmit); } function handleSubmit(e) { e.preventDefault(); // blokuję automatyczne wysłanie formularza, by móc sprawdzić ewentualne błędy // jeśli pole nie zostało wypełnione... if (formEl.firstName.value.length === 0) { // ...wyświetl alert z treścią komunikatu alert('Pole Imię nie może być puste!'); } else if (formEl.lastName.value.length === 0) { alert('Pole Nazwisko nie może być puste!'); } else if (formEl.street.value.length === 0) { alert('Pole Ulica nie może być puste!'); } // itd. }
Jeśli jednak użytkownik pomyli się w kilku polach i podsumuje formularz (kliknie „wyślij”, „dalej” itp.), otrzyma alert z pierwszego niepoprawnego pola. Poprawi błąd, znów kliknie przycisk podsumowujący i znów dostanie alert – tym razem z kolejnego pola. Ten proces będzie musiał powtarzać tak długo, aż wszystkie pola będą poprawne.
Niezbyt przyjazne pod kątem UX (doświadczenia użytkownika, ang. user experience), prawda?
Możemy też zarzucić użytkownika kilkoma alertami naraz, które będzie musiał wyłączyć jeden po drugim:
if (formEl.firstName.value.length === 0) { alert('Pole Imię nie może być puste!'); } if (formEl.lastName.value.length === 0) { alert('Pole Nazwisko nie może być puste!'); } if (formEl.street.value.length === 0) { alert('Pole Ulica nie może być puste!'); } // itd.
Z jednej strony lepiej: użytkownik od razu otrzymuje informację o wszystkich błędach. Lecz co, jeśli przy poprawkach zapomni, gdzie jeszcze się pomylił? Nadal więc UX cierpi!
Kod powyższego rozwiązania znajdziesz w repozytorium, a działanie podejrzysz tutaj. Aby testować kolejne rozwiązania razem ze mną, sforkuj repozytorium i uruchom u siebie.
W pliku HTML stwórzmy miejsce na komunikaty, czyli listę nieuporządkowaną <ul>:
<!-- novalidate – wyłączam sprawdzanie poprawności danych przez przglądarkę, aby swobodnie testować własne rozwiązanie --> <form action="" method="post" novalidate> <ul class="messages"></ul> <div> <label> Imię <input name="firstName" pattern="^[a-zA-Z –-]+$" required /> </label> </div> <!-- (...) -->
Dzięki temu, gdy któreś z pól zostanie błędnie wypełnione, od razu będziemy mogli utworzyć nowy element listy (czyli <li>), uzupełnić go treścią komunikatu i dodać do drzewa DOM. W ten sposób użytkownik dostanie pełną listę błędów.
const formEl = document.querySelector('form'); // wyszukuję w drzewie DOM listę dla komunikatów const messagesList = document.querySelector('.messages'); // sprawdzam, czy formularz został wyszukany i dopiero przypisuję nasłuchiwanie zdarzenia submit if (formEl) { formEl.addEventListener('submit', handleSubmit); } function handleSubmit(e) { e.preventDefault(); // blokuję automatyczne wysłanie formularza, by móc sprawdzić ewentualne błędy messagesList.innerText = ""; // czyszczę listę błędów po każdym submicie formularza, by wyświetlić tylko błędy bieżące if (formEl.firstName.value.length === 0) { // tworzę nowy element listy const liEl = document.createElement('li'); // dodaję do elementu listy treść: komunikat liEl.innerHTML = 'Pole Imię nie może być puste'; // dodaję element listy komunikatów messagesList.appendChild(liEl); } if (formEl.lastName.value.length === 0) { const liEl = document.createElement('li'); liEl.innerHTML = 'Pole Nazwisko nie może być puste'; messagesList.appendChild(liEl); } if (formEl.street.value.length === 0) { const liEl = document.createElement('li'); liEl.innerHTML = 'Pole Ulica nie może być puste'; messagesList.appendChild(liEl); } // itd. }
Zwróć uwagę, że za każdym uruchomieniem funkcji handleSubmit czyszczę listę <ul> – ustawiam jej właściwość innerText na pusty string. Dzięki temu komunikaty błędów w DOM-ie nie będą się duplikować.
Jeżeli chcesz podejrzeć działanie powyższego kodu, to w pliku HTML w elemencie <script> podmień wartość atrybutu src na "errorslist.js".
Powyższe rozwiązanie możemy jeszcze usprawnić – nie powielać kodu tworzącego elementy <li> i skrócić instrukcje warunkowe. Z pomocą przychodzą nam tablice.
Spójrz, w poniższym kodzie pojawiła się zmienna errors przechowująca pustą tablicę. To do niej będziemy dodawać komunikaty błędów.
const formEl = document.querySelector('form'); // wyszukuję w drzewie DOM listę dla komunikatów const messagesList = document.querySelector('.messages'); // sprawdzam, czy formularz został wyszukany i dopiero przypisuję nasłuchiwanie zdarzenia submit if (formEl) { formEl.addEventListener('submit', handleSubmit); } function handleSubmit(e) { e.preventDefault(); // blokuję automatyczne wysłanie formularza, by móc sprawdzić ewentualne błędy messagesList.innerText = ''; // czyszczę listę błędów po każdym submicie formularza, by wyświetlić tylko błędy bieżące const errors = []; // tworzę tablicę, w której będę zbierać komunikaty błędów if (formEl.firstName.value.length === 0) { // jeśli pole nie zostało wypełnione, dodaję komunikat do tablicy `errors` errors.push('Pole Imię nie może być puste'); } if (formEl.lastName.value.length === 0) { errors.push('Pole Nazwisko nie może być puste'); } if (formEl.street.value.length === 0) { errors.push('Pole Ulica nie może być puste'); } // itd. // jeśli tablica z błędami nie jest pusta, to iteruję po niej i tworzę elementy <li> dla każdego dodanego do niej komunikatu if (errors.length !== 0) { errors.forEach(function (error) { // tworzę nowy element listy const liEl = document.createElement('li'); // dodaję do elementu listy treść: komunikat liEl.innerHTML = error; // dodaję element listy komunikatów messagesList.appendChild(liEl); }); } }
Dzięki zapisywaniu komunikatów w tablicy, mogliśmy znacznie skrócić instrukcje warunkowe. Teraz nie powtarzamy już kodu tworzącego elementy <li>. Zawarliśmy go w jednym miejscu: wewnątrz funkcji wywoływanej w metodzie forEach. Iterujemy po tablicy errors i dla każdego komunikatu w niej zawartego tworzymy nowy element <li>.
Efekt jest taki sam, jak wcześniej (błędy wyświetlają się na liście), lecz teraz nasz kod jest trochę łatwiejszy w obsłudze.
Jeżeli chcesz podejrzeć działanie powyższego kodu, to w pliku HTML w elemencie <script> podmień wartość atrybutu src na "errorsarray.js".
Może nam zależeć na tym, aby wyświetlać błędy nie na ogólnej liście, a bezpośrednio pod polem formularza, które zostało błędnie wypełnione. Wówczas możemy gromadzić błędy w tablicy przypisanej do właściwości obiektu o nazwie pola, czyli w takiej formie:
// tworzę obiekt, którego właściwości odpowiadają nazwom pól formularza const errors = { firstName: [], lastName: [], street: [], };
Zobaczmy, jak obsługa takich błędów może wyglądać w kodzie:
const formEl = document.querySelector('form'); // wyszukuję w drzewie DOM listę dla komunikatów const messagesList = document.querySelector('.messages'); // sprawdzam, czy formularz został wyszukany i dopiero przypisuję nasłuchiwanie zdarzenia submit if (formEl) { formEl.addEventListener('submit', handleSubmit); } function handleSubmit(e) { e.preventDefault(); // blokuję automatyczne wysłanie formularza, by móc sprawdzić ewentualne błędy messagesList.innerText = ''; // czyszczę listę błędów po każdym submicie formularza, by wyświetlić tylko błędy bieżące // tworzę obiekt, którego właściwości odpowiadają nazwom pól formularza const errors = { firstName: [], lastName: [], street: [], }; if (formEl.firstName.value.length === 0) { // jeśli pole nie zostało wypełnione, dodaję komunikat do tablicy o odpowiedniej nazwie w obiekcie `errors` errors.firstName.push('Pole Imię nie może być puste'); } if (formEl.lastName.value.length === 0) { errors.lastName.push('Pole Nazwisko nie może być puste'); } if (formEl.street.value.length === 0) { errors.street.push('Pole Ulica nie może być puste'); } // itd. // dla przykładu dodajmy kolejny warunek, który sprawdza, czy pole zawiera same litery if (!formEl.firstName.value.match(/^[a-zA-Z –-]+$/)) { // jeśli pole jest błędnie wypełnione, dodaję komunikat do tablicy o odpowiedniej nazwie w obiekcie `errors` errors.firstName.push('Pole Imię może zawierać tylko litery!'); } // dla każdego klucza w obiekcie `errors`... for (const field in errors) { // sprawdzam, czy mam już elementy <p> z błędami: // -> formEl[field] to pole formularza w DOM-ie (korzystamy z tego, że nazwa klucza w obiekcie i nazwa pola w pliku HTML są takie same); // -> jego parentElement to <label> – wewnątrz niego szukamy elementów <p> (dodaję je poniżej w przypadku błędów) const errorMsgEls = formEl[field].parentElement.querySelectorAll('p'); if (errorMsgEls.length !== 0) { // jeśli takie elementy <p> są, to je usuwam, by się nie zduplikowały Array.from(errorMsgEls).forEach(element => element.remove()); } if (errors[field].length !== 0) { // iteruję po każdej tablicy z błędami dla danego pola, jeśli nie jest ona pusta errors[field].forEach(function (message) { // tworzę element <p> dla błędu const errorMsgEl = document.createElement('p'); // uzupełniam go treścią błędu errorMsgEl.innerText = message; // dodaję element <p> do elemntu <label>, który jest rodzicem dla elementu <input> formEl[field].parentElement.appendChild(errorMsgEl); }); } } }
Ponieważ nasze błędy są teraz przechowywane w obiekcie, używamy pętli for…in, dzięki której możemy iterować po kluczach obiektu. W pętli sprawdzamy, czy któraś z tablic zawiera komunikaty o błędach. Jeśli tak, to wyświetlamy je w postaci paragrafów (elementów <p>) pod adekwatnym polem.
Zobacz: dzięki temu, że nazwy kluczy w obiekcie (firstName, lastName, street) odpowiadają nazwom pól formularza w pliku HTML, mogliśmy wyszukać je w DOM-ie automatycznie: formEl[field]
w pętli zostanie podmienione na formEl["firstName"]
, formEl["lastName"]
, formEl["street"]
itd.
Warto zapamiętać ten sposób, bo przyda się na dalszym etapie nauki.
Jeżeli chcesz podejrzeć działanie powyższego kodu, to w pliku HTML w elemencie <script> podmień wartość atrybutu src na "errorsobject.js".
Jeżeli rozumiesz powyższe przykłady i chcesz dowiedzieć się, jak tworzyć mniej instrukcji warunkowych przy sprawdzaniu pól formularza, zapraszam Cię do artykułu: „Walidacja formularza w JavaScript”.
Udostępnij ten artykuł: