Sprawdź jak walidować formularze w czystym JS!

Spis treści

  1. Wprowadzenie
  2. Walidowanie formularzy
    1. Baza pod przykłady
    2. Customowe wiadomości dla walidacji standardowej
    3. Customowy sposób prezentacji rezultatów walidacji
    4. Klikalny przykład na Github Pages
    5. Pełny kod przykładu na Github
  3. Podsumowanie

Wprowadzenie

Walidowanie formularzy nie zawsze jest prostym zadaniem – najczęściej wyłączamy domyślną walidację i piszemy bardzo customowy kod. Okazuje się jednak, że JS ma wbudowane Constraint Validation API a także umożliwia prostą zmianę domyślnych tekstów które pokazywane są użytkownikowi podczas domyślnej walidacji formularza.

Walidowanie formularzy

Baza pod przykłady

Bazą będzie formularz HTML zawierający pola różnego typu:

<form>
  <div class="form-group">
    <label for="name">
      Name
    </label>
    <input id="name" name="name" type="text" minlength="2" maxlength="10" required>
    <div class="form-group__error"></div>
  </div>
  <!-- Inne pola -->
</form>

Potrzebny nam będzie również checkbox za pomocą którego będziemy wybierać, czy chcemy użyć natywnej walidacji HTML czy też dodać atrybut novalidate do formularza i zawsze wywoływać funkcję submit:

<label for="nativeValidation">
  Native validation?
  <input type="checkbox" id="nativeValidation" checked />
</label>

const nativeValidationCheckbox = document
  .querySelector('#nativeValidation');
nativeValidationCheckbox
  .addEventListener('change', () => {
    form.toggleAttribute('novalidate', !nativeValidationCheckbox.checked);
  });

Customowe wiadomości dla walidacji standardowej

Jeśli odpowiada nam aktualny sposób walidacji który HTML ma wbudowany, możemy tylko zaktualizować wiadomości pokazywane użytkownikowi.

Zmiany tej możemy dokonać dzięki funkcji setCustomValidity.

Customową wiadomość ustawiać możemy na przykład w momencie, gdy użytkownik wpisuje dane w polu:

const validateInput = input => {
  const customValidity = getCustomValidity(input);
  input.setCustomValidity(customValidity);
}
    
form.addEventListener('input', e => {
  const input = e.target;
  input
    .parentElement
    .querySelector('.form-group__error').innerHTML = '';
  validateInput(input);
});

W jaki sposób natomiast stworzymy wiadomości? Bazując na interfejsie ValidityState, dzięki któremu każdy input zawiera informację o błędach walidacji:

const getCustomValidity = input => {
  const validity = input.validity;
  const name = input.name.charAt(0).toUpperCase() + input.name.slice(1);
  if (validity.valueMissing) {
    return `${name} is needed for account`;
  }
  if (validity.tooShort) {
    return `Please provide at least ${input.minLength} characters for ${name}`;
  }
  if (validity.tooLong) {
    return `Please provide at most ${input.maxLength} characters for ${name}`;
  }
  if (validity.rangeUnderflow) {
    return `Please provide value which is at least ${input.min} for ${name}`;
  }
  if (validity.rangeOverflow) {
    return `Please provide value which is at most ${input.max} for ${name}`;
  }
  if (validity.typeMismatch) {
    if (input.type === 'email') {
      return `Please provide a valid email - copy it from your mailbox`;
    }
    if (input.type === 'url') {
      `Please provide a valid url - copy it from browser address bar`;
    }
 }
 return '';
}

Dzięki temu, przykładowo jeśli podamy tylko jeden znak gdy wymagane są co najmniej dwa, pojawi się zdefiniowana przez nas wiadomość:

Customowy sposób prezentacji rezultatów walidacji

Co jednak, jeśli nie odpowiada nam natywna walidacja? Możemy wyłączyć ją dodając atrybut novalidate na formularzu. W takiej sytuacji walidować musimy ręcznie, np. w reakcji na wysłanie formularza. Do walidacji możemy reużyć sposobu z poprzedniego paragrafu bazującego na validity:

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

  const inputs = Array
    .from(e.currentTarget)
    .filter(el => el.id); // to remove button

  for (const input of inputs) {
    const customValidity = getCustomValidity(input);
    if (customValidity) {
      input
        .parentElement
        .querySelector('.form-group__error')
        .innerHTML = customValidity;
      return;
    }
  }

  const values = inputs
    .reduce((acc, current) => ({
      ...acc,
      [current.id]: current.value,
   }), {});
  e.currentTarget.reset();
  alert(JSON.stringify(values));
});

W funkcji przechodzimy przez wszystkie inputy, jeżeli dla któregoś mamy wiadomość o błędzie walidacji wtedy wyświetlamy ją i wychodzimy z funkcji:

A jeśli błędów nie ma, wtedy za pomocą funkcji reduce transformujemy dane w formularzu do obiektu i wyświetlamy je alertem.

Klikalny przykład na Github Pages

Klikalny przykład znajduje się pod adresem:

https://radek-anuszewski.github.io/custom-validation-demo/

Pełny kod przykładu na Github

Pełen kod przykładu znajduje się pod adresem:

https://github.com/radek-anuszewski/custom-validation-demo

Podsumowanie

Walidując formularze nie zawsze musimy polegać na frameworkach czy bibliotekach – wbudowane w JS mechanizmy potrafią całkiem sporo i warto je znać oraz stosować.