Форма с валидацией

HTML-форма с JS-валидацией в реальном времени — проверка при submit и при blur поля, показ ошибок рядом с полем.

Задача

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

Решение

<form class="form" id="signupForm" novalidate>
  <div class="form__group">
    <label class="form__label" for="email">Email</label>
    <input class="form__input" type="email" id="email" name="email"
           placeholder="you@example.com" autocomplete="email" />
    <span class="form__error" id="emailError" aria-live="polite"></span>
  </div>

  <div class="form__group">
    <label class="form__label" for="password">Пароль</label>
    <input class="form__input" type="password" id="password" name="password"
           placeholder="Минимум 8 символов" autocomplete="new-password" />
    <span class="form__error" id="passwordError" aria-live="polite"></span>
  </div>

  <button class="form__submit" type="submit">Создать аккаунт</button>
</form>
.form { max-width: 400px; display: flex; flex-direction: column; gap: 20px; }

.form__group { display: flex; flex-direction: column; gap: 6px; }

.form__label { font-size: 0.875rem; font-weight: 500; color: #374151; }

.form__input {
  padding: 10px 14px;
  border: 1px solid #d1d5db;
  border-radius: 8px;
  font-size: 0.95rem;
  transition: border-color 0.15s;
}

.form__input:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,0.15); }
.form__input.invalid { border-color: #ef4444; }
.form__input.valid   { border-color: #22c55e; }

.form__error { font-size: 0.8rem; color: #ef4444; min-height: 1.2em; }

.form__submit {
  padding: 12px;
  background: #3b82f6;
  color: #fff;
  border: none;
  border-radius: 8px;
  font-size: 1rem;
  cursor: pointer;
}
.form__submit:hover { background: #2563eb; }
const form = document.getElementById('signupForm');

const rules = {
  email: {
    el:     => document.getElementById('email'),
    error:  => document.getElementById('emailError'),
    validate(value) {
      if (!value) return 'Email обязателен';
      if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return 'Неверный формат email';
      return '';
    },
  },
  password: {
    el:     => document.getElementById('password'),
    error:  => document.getElementById('passwordError'),
    validate(value) {
      if (!value) return 'Пароль обязателен';
      if (value.length < 8) return 'Минимум 8 символов';
      return '';
    },
  },
};

function validateField(name) {
  const { el, error, validate } = rules[name];
  const input = el;
  const msg   = validate(input.value.trim());
  const errEl = error;

  errEl.textContent = msg;
  input.classList.toggle('invalid', !!msg);
  input.classList.toggle('valid', !msg && input.value.length > 0);

  return !msg;
}

// Валидация при потере фокуса
Object.keys(rules).forEach((name) => {
  rules[name].el.addEventListener('blur', () => validateField(name));
  rules[name].el.addEventListener('input', () => {
    if (rules[name].el.classList.contains('invalid')) validateField(name);
  });
});

// Валидация при submit
form.addEventListener('submit', (e) => {
  e.preventDefault();

  const valid = Object.keys(rules).map(validateField).every(Boolean);
  if (!valid) return;

  console.log('Форма валидна, отправляем...');
  // fetch('/api/signup', { method: 'POST', body: new FormData(form) });
});

Ключевые моменты

  • novalidate на форме — отключает браузерную валидацию, чтобы использовать кастомную.
  • aria-live="polite" на .form__error — screen reader озвучит появление ошибки.
  • Валидация в blur (не при вводе) — не мешать пользователю в процессе набора.
  • Повторная валидация при input только если поле уже помечено как invalid — убрать ошибку как только исправили.

Варианты

  • Нативная валидация без JS: required, type="email", minlength, pattern — плохо стилизуется.
  • React Hook Form — лучший выбор для React-форм: минимум ре-рендеров, встроенная валидация.
  • Zod — схемная валидация на TypeScript с runtime-проверкой.

Связанные рецепты / темы