Формы — это ворота между пользователем и вашим сайтом. Если они кривые и глупые, посетитель уйдёт. Если умные и вежливые — останется и сделает заказ. Сегодня разберём, как сделать форму, которая сама проверяет данные, не перезагружая страницу, и подсказывает, что не так.

Digital-студия WNDER

Зачем вообще нужна валидация на клиенте?

Представьте, что вы заполняете анкету на бумаге, а вам говорят: «Ошибка, перепишите всё заново». Бесит? Вот и пользователи злятся, когда форма отправляется, сервер думает три секунды и возвращает «неверный email». Клиентская валидация решает это мгновенно: пока вы печатаете, скрипт уже проверил поле и мягко намекнул, что что-то не так.

Плюсы очевидны: меньше нагрузки на сервер, быстрая обратная связь, удобство. Но есть и подводный камень: никогда не доверяйте только клиентской проверке. Злоумышленник может отключить JavaScript или отправить запрос напрямую. Поэтому серверная валидация обязательна, но клиентская — это ваш первый рубеж обороны и заботы о пользователе.

Скелет формы: HTML и немного CSS

Начнём с простого: форма регистрации с полями «Имя», «Email», «Пароль» и кнопкой. Никаких лишних украшательств, только необходимое.

<form id="signupForm" novalidate>
  <div class="field">
    <label for="name">Имя</label>
    <input type="text" id="name" name="name" placeholder="Ваше имя">
    <span class="error" id="nameError"></span>
  </div>

  <div class="field">
    <label for="email">Email</label>
    <input type="email" id="email" name="email" placeholder="you@example.com">
    <span class="error" id="emailError"></span>
  </div>

  <div class="field">
    <label for="password">Пароль</label>
    <input type="password" id="password" name="password" placeholder="Минимум 6 символов">
    <span class="error" id="passwordError"></span>
  </div>

  <button type="submit">Зарегистрироваться</button>
</form>

Атрибут novalidate отключает встроенную браузерную валидацию — она у нас будет своя, более гибкая. Каждое поле обёрнуто в div.field, а рядом с инпутом лежит span.error для вывода сообщения.

Добавим немного CSS, чтобы ошибки выглядели заметно, но не пугающе:

<style>
  .field { margin-bottom: 1rem; }
  .error { color: #d32f2f; font-size: 0.9rem; display: block; margin-top: 4px; }
  input.invalid { border-color: #d32f2f; background-color: #fff6f6; }
  input.valid { border-color: #2e7d32; }
</style>

Теперь поля при ошибке будут подсвечиваться красным, а при успехе — зелёным. Пользователь сразу видит, где косяк.

Пишем логику валидации на JavaScript

Основная идея: на каждое поле вешаем обработчик события input (или blur, чтобы проверять при уходе с поля). Функция валидации смотрит на значение и решает, пропускать или ругаться.

Начнём с простых проверок:

  • Имя — не пустое, минимум 2 символа, только буквы и пробелы.
  • Email — соответствует регулярному выражению (простая проверка на собаку и точку).
  • Пароль — минимум 6 символов, хотя бы одна цифра и одна буква.

Вот функция, которая проверяет имя:

function validateName(value) {
  if (value.trim() === '') {
    return 'Имя не может быть пустым';
  }
  if (value.trim().length < 2) {
    return 'Имя должно содержать минимум 2 символа';
  }
  if (!/^[a-zA-Zа-яА-Я\s]+$/.test(value)) {
    return 'Имя может содержать только буквы и пробелы';
  }
  return ''; // пустая строка = ошибок нет
}

Аналогично для email и пароля. Для email используем простое регулярное выражение:

function validateEmail(value) {
  const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!re.test(value)) {
    return 'Введите корректный email';
  }
  return '';
}

Пароль:

function validatePassword(value) {
  if (value.length < 6) {
    return 'Пароль должен быть не короче 6 символов';
  }
  if (!/\d/.test(value)) {
    return 'Пароль должен содержать хотя бы одну цифру';
  }
  if (!/[a-zA-Z]/.test(value)) {
    return 'Пароль должен содержать хотя бы одну букву';
  }
  return '';
}

Теперь нужно связать эти функции с полями. Напишем общий обработчик, который будет вызываться при вводе и при потере фокуса.

const form = document.getElementById('signupForm');
const nameInput = document.getElementById('name');
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');

const nameError = document.getElementById('nameError');
const emailError = document.getElementById('emailError');
const passwordError = document.getElementById('passwordError');

function showError(input, errorElement, message) {
  if (message) {
    input.classList.add('invalid');
    input.classList.remove('valid');
    errorElement.textContent = message;
  } else {
    input.classList.remove('invalid');
    input.classList.add('valid');
    errorElement.textContent = '';
  }
}

function validateField(input, errorElement, validator) {
  const message = validator(input.value);
  showError(input, errorElement, message);
  return message === '';
}

// Привязываем события
nameInput.addEventListener('input', () => {
  validateField(nameInput, nameError, validateName);
});
emailInput.addEventListener('input', () => {
  validateField(emailInput, emailError, validateEmail);
});
passwordInput.addEventListener('input', () => {
  validateField(passwordInput, passwordError, validatePassword);
});

Теперь при каждом нажатии клавиши поле проверяется. Но это может быть навязчиво: пользователь ещё не допечатал email, а мы уже орём «неверный формат». Поэтому часто валидацию делают на событие blur (когда фокус уходит с поля). Можно комбинировать: первый раз проверяем на blur, а потом уже на input. Но для простоты оставим input — современные пользователи привыкли к мгновенной реакции.

Правило: Никогда не показывайте ошибку, пока пользователь не закончил вводить. Лучше проверять на blur или с задержкой (debounce) после ввода. Иначе вы будете ругаться на каждую букву.

Обработка отправки формы

Когда пользователь нажимает «Зарегистрироваться», нужно проверить все поля ещё раз. Если есть ошибки — не отправлять, показать сообщение. Если всё чисто — можно отправлять данные на сервер (через fetch или обычный submit).

form.addEventListener('submit', (event) => {
  event.preventDefault(); // отменяем стандартную отправку

  const isNameValid = validateField(nameInput, nameError, validateName);
  const isEmailValid = validateField(emailInput, emailError, validateEmail);
  const isPasswordValid = validateField(passwordInput, passwordError, validatePassword);

  if (isNameValid && isEmailValid && isPasswordValid) {
    // Все поля валидны — отправляем данные
    const formData = new FormData(form);
    console.log('Отправка:', Object.fromEntries(formData));
    // Здесь можно сделать fetch('/api/register', { method: 'POST', body: formData })
    alert('Форма успешно отправлена!');
  } else {
    // Подсвечиваем первое невалидное поле
    if (!isNameValid) nameInput.focus();
    else if (!isEmailValid) emailInput.focus();
    else if (!isPasswordValid) passwordInput.focus();
  }
});

Обратите внимание: мы вызываем event.preventDefault(), чтобы страница не перезагружалась. Это основа любой интерактивной формы.

Продвинутые приёмы и лайфхаки

Вот несколько фишек, которые сделают вашу форму ещё удобнее.

  • Визуальная обратная связь. Добавьте иконки (галочка/крестик) или меняйте цвет рамки. Мы уже сделали через классы .valid и .invalid.
  • Подсказки под полем. Вместо ошибки можно показывать требования к паролю заранее. Например, серым текстом: «Минимум 6 символов, хотя бы одна цифра».
  • Блокировка кнопки. Если поля невалидны, кнопка отправки может быть неактивна (disabled). Но это спорно: пользователь может не понять, почему кнопка серая. Лучше оставить активной, но при клике показывать ошибки.
  • Debounce. Если валидация тяжёлая (например, проверка занятости логина через AJAX), используйте задержку. Простая функция debounce:
function debounce(fn, delay) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => fn.apply(this, args), delay);
  };
}

И применяйте её к обработчику input: nameInput.addEventListener('input', debounce(() => validateField(...), 300));

Чтобы систематизировать знания, сведём основные параметры валидации в таблицу.

Поле Правило Пример регулярного выражения
Имя Только буквы и пробелы, от 2 символов /^[a-zA-Zа-яА-Я\s]+$/
Email Наличие @ и точки после неё /^[^\s@]+@[^\s@]+\.[^\s@]+$/
Пароль Минимум 6 символов, цифра и буква Проверка через /\d/ и /[a-zA-Z]/
Лайфхак: Используйте атрибут aria-invalid и aria-describedby для доступности. Скринридеры будут читать ошибки вслух. Пример: input.setAttribute('aria-invalid', 'true'); и связать с span.error через aria-describedby.

И напоследок: не забывайте про серверную валидацию. Даже самая умная клиентская проверка обходится в два клика. Всегда дублируйте правила на бэкенде.

Что в итоге?

Мы сделали интерактивную форму с валидацией на чистом JavaScript. Теперь она:

  • Мгновенно реагирует на ввод и показывает ошибки.
  • Подсвечивает поля зелёным или красным.
  • Не отправляется, пока все данные не корректны.

Главное — не переусердствовать. Валидация должна помогать, а не раздражать. Проверяйте на blur или с задержкой, пишите понятные сообщения об ошибках, и пользователи скажут спасибо. А если хотите глубже — изучайте библиотеки вроде Just-validate или Yup, но начинать всегда лучше с основ.

Студия WNDER