Назад

AJAX-отправка форм с валидацией на jQuery

Главная
Блог
AJAX-отправка форм с валидацией на jQuery

В современном вебе AJAX-отправка форм стала стандартом, позволяя обновлять содержимое страницы без её перезагрузки. В этой статье мы разберём, как реализовать надёжное решение для отправки форм с валидацией данных, обработкой ошибок и защитой от спама.

Базовая HTML-структура формы

Для начала создадим HTML-структуру нашей формы:

<form class="js-form" action="/submit-form" method="POST">
  <div class="form-group">
    <label for="name">Ваше имя:</label>
    <input class="form-input inp-req" type="text" name="name" id="name" required>
    <div class="error-message"></div>
  </div>
  
  <div class="form-group">
    <label for="phone">Ваш телефон:</label>
    <input class="form-input inp-req" type="tel" name="phone" id="phone" required>
    <div class="error-message"></div>
  </div>
  
  <div class="form-group">
    <label for="email">Email:</label>
    <input class="form-input inp-req" type="email" name="email" id="email" required>
    <div class="error-message"></div>
  </div>
  
  <input type="hidden" name="csrf_token" value="<?= csrf_token() ?>">
  
  <button class="btn-submit js-submit" type="submit">Отправить</button>
  
  <div class="form-messages"></div>
</form>

CSS для оформления ошибок

Добавим базовые стили для отображения ошибок:

.form-input.error {
  border-color: #ff3860;
}

.error-message {
  color: #ff3860;
  font-size: 0.875rem;
  margin-top: 0.25rem;
  display: none;
}

.error-message.active {
  display: block;
}

.form-messages {
  padding: 1rem;
  margin: 1rem 0;
  border-radius: 4px;
  display: none;
}

.form-messages.success {
  background-color: #effaf0;
  color: #257942;
  display: block;
}

.form-messages.error {
  background-color: #feecf0;
  color: #cc0f35;
  display: block;
}

.btn-submit.loading {
  position: relative;
  pointer-events: none;
}

.btn-submit.loading:after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border: 2px solid transparent;
  border-top-color: currentColor;
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

Полный JavaScript-код с валидацией

Теперь реализуем основную логику на jQuery:

(function($){
  // Валидация email
  function isValidEmail(email) {
    const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return re.test(email);
  }

  // Валидация телефона (базовая)
  function isValidPhone(phone) {
    const re = /^[\d\+][\d\s\-\(\)]{7,}\d$/;
    return re.test(phone.replace(/\s/g, ''));
  }

  // Показать сообщение об ошибке
  function showError($field, message) {
    $field.addClass('error');
    const $errorMsg = $field.siblings('.error-message');
    $errorMsg.text(message).addClass('active');
  }

  // Скрыть сообщение об ошибке
  function hideError($field) {
    $field.removeClass('error');
    $field.siblings('.error-message').removeClass('active');
  }

  // Показать глобальное сообщение
  function showFormMessage($form, text, type) {
    const $msgBox = $form.find('.form-messages');
    $msgBox.removeClass('success error').addClass(type).text(text);
  }

  // Обработка отправки формы
  $('.js-form').on('submit', function(e) {
    e.preventDefault();
    const $form = $(this);
    const $submitBtn = $form.find('.js-submit');
    let hasErrors = false;

    // Валидация полей
    $form.find('.inp-req').each(function() {
      const $field = $(this);
      const value = $field.val().trim();
      const fieldName = $field.attr('name');

      hideError($field);

      if (!value) {
        showError($field, 'Это поле обязательно для заполнения');
        hasErrors = true;
        return;
      }

      // Специфическая валидация для разных типов полей
      switch(fieldName) {
        case 'email':
          if (!isValidEmail(value)) {
            showError($field, 'Введите корректный email');
            hasErrors = true;
          }
          break;
          
        case 'phone':
          if (!isValidPhone(value)) {
            showError($field, 'Введите корректный телефон');
            hasErrors = true;
          }
          break;
          
        case 'name':
          if (value.length < 2) {
            showError($field, 'Имя должно содержать минимум 2 символа');
            hasErrors = true;
          }
          break;
      }
    });

    if (hasErrors) {
      showFormMessage($form, 'Пожалуйста, исправьте ошибки в форме', 'error');
      return false;
    }

    // Блокируем кнопку отправки
    $submitBtn.addClass('loading').prop('disabled', true);

    // AJAX-отправка
    $.ajax({
      url: $form.attr('action'),
      type: 'POST',
      data: $form.serialize(),
      dataType: 'json'
    })
    .done(function(response) {
      if (response.success) {
        $form[0].reset();
        showFormMessage($form, response.message || 'Форма успешно отправлена!', 'success');
      } else {
        // Обработка серверных ошибок
        if (response.errors) {
          Object.keys(response.errors).forEach(field => {
            const $field = $form.find(`[name="${field}"]`);
            showError($field, response.errors[field][0]);
          });
        }
        showFormMessage($form, response.message || 'Ошибка при отправке формы', 'error');
      }
    })
    .fail(function(xhr) {
      console.error('Error:', xhr.responseText);
      showFormMessage($form, 'Произошла ошибка при соединении с сервером', 'error');
    })
    .always(function() {
      $submitBtn.removeClass('loading').prop('disabled', false);
    });

    return false;
  });

  // Валидация при потере фокуса
  $('.inp-req').on('blur', function() {
    const $field = $(this);
    const value = $field.val().trim();
    
    if (!value) return;
    
    if ($field.attr('name') === 'email' && !isValidEmail(value)) {
      showError($field, 'Введите корректный email');
    } else if ($field.attr('name') === 'phone' && !isValidPhone(value)) {
      showError($field, 'Введите корректный телефон');
    }
  });
  
  // Очистка ошибок при вводе
  $('.inp-req').on('input', function() {
    const $field = $(this);
    if ($field.hasClass('error')) {
      hideError($field);
    }
  });

})(jQuery);

Дополнительные улучшения

1. Защита от спама

Добавьте в форму скрытое поле (honeypot):

<input type="text" name="honeypot" style="display:none !important" tabindex="-1" autocomplete="off">

И проверку в JavaScript:

if ($form.find('input[name="honeypot"]').val() !== '') {
  // Это бот, не отправляем форму
  return false;
}

2. Ограничение частоты отправки

let lastSubmitTime = 0;

// В обработчике submit:
const now = Date.now();
if (now - lastSubmitTime < 5000) { // 5 секунд
  showFormMessage($form, 'Пожалуйста, не отправляйте форму так часто', 'error');
  return false;
}
lastSubmitTime = now;

3. Загрузка файлов

Для загрузки файлов через AJAX:

const formData = new FormData($form[0]);

$.ajax({
  url: $form.attr('action'),
  type: 'POST',
  data: formData,
  processData: false,
  contentType: false,
  // ...
});

Заключение

Представленное решение обеспечивает:

  1. Клиентскую валидацию различных типов полей

  2. Красивый вывод ошибок

  3. Защиту от спама и CSRF-атак

  4. Улучшенный пользовательский опыт

  5. Поддержку загрузки файлов

  6. Обработку серверных ошибок

Этот код можно легко адаптировать под любые формы на вашем сайте, просто добавляя соответствующие классы и настройки валидации.

Нужен надежный исполнитель?
Разрабатываем сайты, выполняем миграцию на Битрикс, дорабатываем функционал, сопровождаем проекты, а также занимаемся поисковым продвижением и комплексным маркетингом
Получить консультацию
Читайте по теме
Все статьи
Нужен надежный исполнитель?
Разрабатываем сайты, выполняем миграцию на Битрикс, дорабатываем функционал, сопровождаем проекты, а также занимаемся поисковым продвижением и комплексным маркетингом
Получить консультацию
Все статьи