Damian Słoński

Inteligentne pola formularzy ✅

W jaki sposób uprzyjemnić użytkownikom wypełnianie formularzy za pomocą natywnych API przeglądarki?

  • web
  • accessibility
Inteligentne pola formularzy ✅

Formularze... nikt nie lubi formularzy 😒

Kto lubi wypełniać formularze? Często są za długie, a co gorsza czasami nawet nie wiadomo jak długie są, dopóki się ich nie wypełni. Najgorzej... 😒 Poza tego typu błędami w UXie, w jaki sposób możemy jako frontend developerzy ułatwić użytkownikom proces uzupełniania formularzy? Jest kilka prostych sposobów, a część z nich jest nawet natywnie zaimplementowana w przeglądarki i gotowa do użycia!

Autocomplete 💡

Mechanizm autouzupełniania pól w większości przeglądarek, które mają dostęp do naszych danych (Chrome & konto Google lub Safari & Apple ID) działa out of the box. Jest kilka rzeczy, na które warto zwrócić uwagę przy implementacji formularzy, aby ten mechanizm działał w pełni poprawnie.

Znaczenie mają tutaj oczywiście atrybuty name, type oraz autocomplete.

W standardowym formularzu logowania, gdzie mamy pole z type="email" oraz type="password" przeglądarka powinna bez problemu na podstawie adresu dopasować dane odpowiedniego konta.

Autouzupełnianie - Logowanie
Autouzupełnianie - Logowanie

Zazwyczaj w takim przypadku przeglądarka koloruje wypełnione pola na charakterystyczny jasnożółty kolor. Możemy zmodyfikować te style za pomocą pseudoselektorów: :autofill / :-webkit-autofill.

Hasła 🔐

Warto pamiętać o tym, żeby w formularzu ustawiania nowego hasła dodać do inputów odpowiednio: autocomplete="new-password" oraz autocomplete="current-password" jeśli jest to wymagane w tym samym kroku. Pozwala to np. zadziałać mechanizmom generowania silnego hasła.

Autouzupełnianie - Silne hasło
Autouzupełnianie - Silne hasło

Kod jednorazowy 📲

Bardzo dużym ułatwieniem jest autouzupełnianie się kodu jednorazowego do dwuetapowej weryfikacji, np. przychodzącego w wiadomości SMS lub z aplikacji 2FA. W bardzo łatwy sposób możemy to zaimplementować za pomocą: autocomplete="one-time-code".

Autouzupełnianie - Kod jednorazowy
Autouzupełnianie - Kod jednorazowy

Dane adresowe 🏡

Adres (miejsce zamieszkania) jest bardzo częstym elementem wielu formularzy i siłą rzeczy wymaga uzupełnieniu wielu pól, takich jak: ulica, miasto, kod pocztowy itp. Dlatego warto pamiętać o tym, aby w każdym polu uzupełnić atrybuty name oraz autocomplete, dzięki czemu może zadziać się magia✨ tego typu:

Autouzupełnianie - Adres
Autouzupełnianie - Adres

Problem z name 👤

Często występujący problem, jaki napotykam to niepoprawna obsługa pola name, kiedy powinien zawierać on w sobie zarówno imię, jak i nazwisko użytkownika.

Domyślnie przeglądarki dla pola z name="name" uzupełniają je nazwiskiem, ale czasami w mniej formalnych formularzach (np. formularz kontaktowy na blogu) możemy chcieć autouzupełniania samego imienia lub gdy nie rozdzielamy imienia i nazwiska na dwa osobne pola, to oczekujemy, że przeglądarka uzupełni nam pole imieniem i nazwiskiem. I bez większego problemu możemy to w pełni scustomizować na następujące sposoby:

  • autocomplete="given-name" - imię
  • autocomplete="full-name" - nazwisko
  • autocomplete="name" - imię i nazwisko

Karta płatnicze 💳

Przepisywanie danych z karty kredytowej lub debetowej również nie należy do najprzyjemniejszych czynności przy uzupełnianiu formularzy. Dzięki odpowiednim atrybutom można to jednak uprościć do kilku kliknięć, które uzupełnią wszystkie podstawowe dane i ręcznego uzupełnienia wyłącznie ostatniego pola z numerem CSV.

Autouzupełnianie - Logowanie
Autouzupełnianie - Logowanie

⚠️ Autouzupełnianie danych karty (ze względów bezpieczeństwa) w niektórych przeglądarkach wymaga, aby strona korzystała z certyfikatu SSL, dlatego może to nie działać przy standardowej konfiguracji serwera developerskiego na http://localhost.

"Brudny i niepoprawny" 🧟‍♂️

Bardzo fajnym rodzajem podpowiedzi jest pokazywanie użytkownikowi na bieżąco, które pola są wypełnione prawidłowo, a które nieprawidłowo. Często walidacja formularzy odbywa się po stronie serwera i następuje dopiero po uzupełnieniu wszystkich pól wraz z ich wysłaniem. Z punktu widzenia wygody użytkownika nie jest to optymalne rozwiązanie, dlatego w miarę możliwości warto delegować część walidacji na frontend. W niektórych przypadkach może to być utrudnione ze względu na fakt, iż walidacja może wymagać informacji z bazy danych i implikuje to potrzebę wysyłki wielu requestów w tle. Jednak podział na walidację podstawową ("techniczną") po stronie klienta oraz zaawansowaną ("merytoryczną") po stronie serwera wydaje się dobrym kompromisem dla UXu oraz ilości pracy.

Walidacja po stronie klienta może sprawdzać, czy pole z hasłem zostało uzupełnione (required) oraz czy dane są "technicznie" poprawne, tzn. czy mają odpowiednią długość, czy email jest stringiem w odpowiedniej formie (x...x@y..y.zz) itp. Wtedy walidacja na backendzie sprawdza już tylko takie rzeczy jak np. to czy istnieje użytkownik powiązany z danym adresem email, czy hasło jest poprawne itp. Scenariusz taki pozwala wyeliminować część potencjalnych pomyłek usera już na etapie wprowadzania ich w przeglądarce, co wpływa pozytywnie na doświadczenia użytkownika oraz może ograniczyć obciążenie serwera.

Bardzo przydatny może okazać się pseudoselektor :invalid, za pomocą którego możemy zmienić stylowanie np. elementu <input> w przypadku, kiedy treść nie spełnia warunków tzw. constrain validation.

1input:invalid {
2 color: red;
3 border-color: red;
4}

Problem z tym rozwiązaniem jest taki, że w większości przypadków początkowa wartość (pusta) będzie nieprawidłowa, a to powoduje, że po wejściu na formularz pola już będą oznaczone jako błędnie wypełnione, co bardziej może wprowadzać użytkowników w zakłopotanie, niż pomagać im wypełnić formularz. Najczęściej ten problem rozwiązywało się przy pomocy JavaScriptu, dodając np. klasę .dirty lub .touched dopiero w momencie, gdy użytkownik wejdzie w interakcję i opuści pole formularza. Istnieje też inne, uproszczone rozwiązanie w CSS, które w niektórych przypadkach może być wystarczające i warto je znać. Jest to wykorzystanie pseudoslektora :placeholder-shown (element <input> lub <textarea> który "aktualnie wyświetla placeholder") oraz :not i :focus. W sumie daje nam to selektor dla elementu, który jednocześnie nie jest poprawnie uzupełniony, nie jest pusty i nie jest aktywny.

1input:not(:focus):not(:placeholder-shown):invalid {
2 color: red;
3 outline-color: red;
4}

Aby to rozwiązanie zadziałało we wszystkich współczesnych przeglądarkach element musi mieć ustawiony atrybut placeholder z jakąkolwiek wartością, może być też pusty string "&nbsp;".

1<input type="text" name="name" placeholder=" " />

Edit 2023: Od listopada 2023 roku pseudoselektor :user-invalid jest wspierany przez wszystkie wspólczesne przeglądarki. 🎉

Istnieje również pseudoselektor :user-invalid, który łączy 3 powyższe w jednym, niestety obecnie działa on tylko w nowych wersjach Firefox'a.

1/* input:not(:focus):not(:placeholder-shown):invalid { */
2input:user-invalid {
3 color: red;
4 border-color: red;
5}

wsparcie przeglądarek: caniuse.com/mdn-css_selectors_user-invalid

Natywna walidacja kodów 🤖

Podczas wpisywania numerów takich jak PESEL czy NIP istnieje całkiem duże prawdopodobieństwo dokonania pomyłki w postaci literówki, którą bardzo ciężko dostrzec gołym okiem. Brzmi jak idealne zadanie dla komputera? 🖥 Tak, ale tego typu pola warto walidować na już na frontendzie. Dla przykładu weźmy sobie numer dowodu osobistego, np.: ASB123456.

W pierwszej kolejności warto ustawić atrybut maxlength na 9, aby uniemożliwić użytkownikowi wpisanie większej ilości znaków.

1<fieldset>
2 <label for="id_number">ID number:</label>
3 <input maxlength="9" id="id_number" type="text" name="id_number" />
4</fieldset>

Numer dowodu osobistego ma zawsze następującą strukturę: 3x wielka litera + 6x cyfra. W bardzo łatwy sposób można dodać walidację takiego wzoru za pomocą atrybutu pattern. Warto też pamiętać o uzupełnieniu atrybutu placeholder.

1<fieldset>
2 <label for="id_number">ID number:</label>
3 <input
4 pattern="[A-Z]{3}[0-9]{6}"
5 placeholder="LLLDDDDDD"
6 maxlength="9"
7 id="id_number"
8 type="text"
9 name="id_number"
10 />
11</fieldset>

Tego typu walidacja zapewne pozwoli uniknąć większości pomyłek, jednak tego typu numery zazwyczaj posiadają tzn. liczbę i sumę kontrolną. Dzięki temu za pomocą odpowiedniego algorytmu możemy obliczyć czy podany numer jest prawidłowy. Dodatkowo korzystając z języka JavaScript możemy ustawić treść wiadomości dla natywnego mechanizmu walidacji w danym środowisku.

1function validPesel(peselNumber) {
2 /* function logic */
3}
4const input = document.querySelector('input[name="pesel"]')
5
6input.addEventListener(change, (event) => {
7 const isValidPesel = validPesel(event.target.value)
8 input.setCustomValidity(isValidPesel ? "Incorrect PESEL number" : "")
9})
Niestandardowa walidacja - PESEL
Niestandardowa walidacja - PESEL

Podsumowanie 📋

Przeglądarki do autouzupełniania mogą wykorzystywać różne atrybuty, takie jak name, type oraz autocomplete, czasami może to być też id lub tekst w przyporządkowanym elemencie <label>, ale jeśli chcemy mieć nad tym kontrolę to najlepiej odpowiednio uzupełnić name i autocomplete. Kiedy formularz nie wykorzystuje dodatkowych, bardziej zaawansowanych narzędzi do walidacji, wciąż możemy w bardzo prosty sposób podnieść jakość wrażeń użytkownika, np. za pomocą pseudoselektora :invalid czy metody setCustomValidity.