В современном вебе 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,
// ...
});Заключение
Представленное решение обеспечивает:
-
Клиентскую валидацию различных типов полей
-
Красивый вывод ошибок
-
Защиту от спама и CSRF-атак
-
Улучшенный пользовательский опыт
-
Поддержку загрузки файлов
-
Обработку серверных ошибок
Этот код можно легко адаптировать под любые формы на вашем сайте, просто добавляя соответствующие классы и настройки валидации.