Обработка форм на чистом JavaScript: без фреймворков

Формы являются основой пользовательского взаимодействия в веб-приложениях. Хотя фреймворки, такие как React и Vue, предлагают удобные абстракции, понимание того, как обрабатывать формы с помощью чистого JavaScript, дает вам полный контроль и устраняет ненужные зависимости.
Это руководство проведет вас через все, что нужно знать о нативной обработке форм в JavaScript — от выбора элементов и получения значений до валидации ввода и обработки отправки.
Ключевые выводы
- Чистый JavaScript предоставляет все необходимые инструменты для полной обработки форм без фреймворков
- Используйте
event.preventDefault()
для управления поведением отправки формы - Используйте HTML5 constraint validation для базовой валидации с минимальным количеством кода
- Реализуйте валидацию в реальном времени с помощью событий
input
иblur
для лучшего пользовательского опыта - FormData API упрощает сбор и обработку значений формы
- Всегда учитывайте доступность при реализации пользовательской валидации форм
Базовая обработка форм с помощью JavaScript
Начнем с простой HTML-формы:
<form id="signup-form">
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username">
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email">
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password">
</div>
<button type="submit">Sign Up</button>
</form>
Выбор элементов формы
Вы можете получить доступ к форме и её элементам несколькими способами:
// Выбор по ID (рекомендуется для конкретных форм)
const form = document.getElementById('signup-form');
// Выбор с помощью querySelector
const form = document.querySelector('#signup-form');
// Доступ к коллекции элементов формы
const forms = document.forms;
const signupForm = forms['signup-form']; // по id/name
const firstForm = forms[0]; // по индексу
Перехват события отправки
Для обработки отправки формы добавьте обработчик события к форме:
const form = document.getElementById('signup-form');
form.addEventListener('submit', function(event) {
// Предотвращаем стандартную отправку формы
event.preventDefault();
// Обрабатываем данные формы здесь
console.log('Form submitted!');
});
Метод preventDefault()
останавливает браузер от отправки формы и обновления страницы, предоставляя вам контроль над тем, что происходит дальше.
Получение значений полей формы
Существует несколько способов получения значений полей формы:
Метод 1: Доступ к отдельным элементам
form.addEventListener('submit', function(event) {
event.preventDefault();
// Получаем значения из отдельных элементов формы
const username = document.getElementById('username').value;
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
console.log(username, email, password);
});
Метод 2: Использование form.elements
Свойство elements
предоставляет доступ ко всем элементам управления формы:
form.addEventListener('submit', function(event) {
event.preventDefault();
// Доступ через коллекцию elements
const username = form.elements.username.value;
const email = form.elements.email.value;
const password = form.elements.password.value;
console.log(username, email, password);
});
Метод 3: Использование FormData API
FormData
API предоставляет удобный способ получения всех значений формы:
form.addEventListener('submit', function(event) {
event.preventDefault();
// Создаем объект FormData из формы
const formData = new FormData(form);
// Доступ к отдельным значениям
const username = formData.get('username');
const email = formData.get('email');
// Преобразование в обычный объект
const formObject = Object.fromEntries(formData);
console.log(formObject); // {username: '...', email: '...', password: '...'}
});
Валидация форм с помощью HTML5 ограничений
Современный HTML5 предоставляет встроенные атрибуты валидации, которые работают без JavaScript:
<form id="signup-form">
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username" required minlength="3">
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required minlength="8"
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).*$">
<small>Password must contain at least 8 characters, including uppercase, lowercase, and numbers</small>
</div>
<button type="submit">Sign Up</button>
</form>
Распространенные атрибуты валидации включают:
required
: Поле должно быть заполненоminlength
/maxlength
: Минимальная/максимальная длина текстаmin
/max
: Минимальные/максимальные значения для числовых полейpattern
: Регулярное выражение для сопоставленияtype="email"
: Валидация формата emailtype="url"
: Валидация формата URL
Использование Constraint Validation API
JavaScript может получить доступ к встроенной системе валидации браузера:
form.addEventListener('submit', function(event) {
// Проверяем, валидна ли форма с помощью Constraint Validation API
if (!form.checkValidity()) {
// Форма невалидна - позволяем браузеру обработать отображение ошибок
return;
}
// Если мы дошли сюда, форма валидна
event.preventDefault();
// Обрабатываем валидные данные формы
console.log('Form is valid, processing data...');
});
Вы также можете проверить валидность конкретных полей:
const emailInput = document.getElementById('email');
// Проверяем, валиден ли email
if (emailInput.validity.valid) {
console.log('Email is valid');
} else {
// Проверяем конкретные ошибки валидации
if (emailInput.validity.typeMismatch) {
console.log('Email format is incorrect');
}
if (emailInput.validity.valueMissing) {
console.log('Email is required');
}
}
Пользовательская валидация и сообщения об ошибках
Хотя HTML5 валидация удобна, часто требуется пользовательская логика валидации и сообщения об ошибках:
form.addEventListener('submit', function(event) {
event.preventDefault();
const username = document.getElementById('username');
const email = document.getElementById('email');
const password = document.getElementById('password');
// Очищаем предыдущие сообщения об ошибках
clearErrors();
let isValid = true;
// Пользовательская валидация имени пользователя
if (username.value.trim() === '') {
displayError(username, 'Username is required');
isValid = false;
} else if (username.value.length < 3) {
displayError(username, 'Username must be at least 3 characters');
isValid = false;
}
// Пользовательская валидация email
if (!isValidEmail(email.value)) {
displayError(email, 'Please enter a valid email address');
isValid = false;
}
// Пользовательская валидация пароля
if (password.value.length < 8) {
displayError(password, 'Password must be at least 8 characters');
isValid = false;
} else if (!/[A-Z]/.test(password.value)) {
displayError(password, 'Password must contain at least one uppercase letter');
isValid = false;
}
// Если валидна, обрабатываем форму
if (isValid) {
console.log('Form is valid, submitting...');
// Отправляем данные формы
}
});
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function displayError(input, message) {
const formControl = input.parentElement;
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.textContent = message;
formControl.appendChild(errorElement);
input.classList.add('error-input');
}
function clearErrors() {
document.querySelectorAll('.error-message').forEach(error => error.remove());
document.querySelectorAll('.error-input').forEach(input => {
input.classList.remove('error-input');
});
}
Валидация в реальном времени с помощью событий ввода
Для лучшего пользовательского опыта валидируйте ввод по мере набора пользователем:
const emailInput = document.getElementById('email');
emailInput.addEventListener('input', function() {
if (this.value && !isValidEmail(this.value)) {
this.setCustomValidity('Please enter a valid email address');
} else {
this.setCustomValidity('');
}
});
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
Событие input
срабатывает всякий раз, когда пользователь изменяет значение, в то время как событие change
срабатывает, когда поле теряет фокус после изменения.
Обработка различных типов полей
Радиокнопки
<div>
<p>Subscription plan:</p>
<label>
<input type="radio" name="plan" value="free" checked> Free
</label>
<label>
<input type="radio" name="plan" value="premium"> Premium
</label>
</div>
// Получаем значение выбранной радиокнопки
const selectedPlan = document.querySelector('input[name="plan"]:checked').value;
// Слушаем изменения
document.querySelectorAll('input[name="plan"]').forEach(radio => {
radio.addEventListener('change', function() {
console.log('Selected plan:', this.value);
});
});
Флажки
<div>
<p>Interests:</p>
<label>
<input type="checkbox" name="interests" value="javascript"> JavaScript
</label>
<label>
<input type="checkbox" name="interests" value="html"> HTML
</label>
<label>
<input type="checkbox" name="interests" value="css"> CSS
</label>
</div>
// Получаем все отмеченные значения
function getSelectedInterests() {
const checkboxes = document.querySelectorAll('input[name="interests"]:checked');
const values = Array.from(checkboxes).map(cb => cb.value);
return values;
}
// При отправке формы
form.addEventListener('submit', function(event) {
event.preventDefault();
const interests = getSelectedInterests();
console.log('Selected interests:', interests);
});
Выпадающие списки
<div>
<label for="country">Country:</label>
<select id="country" name="country">
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="ca">Canada</option>
<option value="uk">United Kingdom</option>
</select>
</div>
// Получаем выбранное значение
const country = document.getElementById('country').value;
// Слушаем изменения
document.getElementById('country').addEventListener('change', function() {
console.log('Selected country:', this.value);
});
Полный пример обработки формы
Давайте объединим все в полном примере:
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('signup-form');
// Добавляем валидацию в реальном времени
setupRealTimeValidation();
form.addEventListener('submit', function(event) {
event.preventDefault();
if (validateForm()) {
// Создаем объект с данными формы
const formData = new FormData(form);
const userData = Object.fromEntries(formData);
// Здесь обычно отправляют данные на сервер
console.log('Submitting user data:', userData);
// Имитируем API вызов
submitFormData(userData)
.then(response => {
showSuccessMessage('Account created successfully!');
form.reset();
})
.catch(error => {
showErrorMessage('Failed to create account. Please try again.');
});
}
});
function validateForm() {
// Очищаем предыдущие ошибки
clearErrors();
let isValid = true;
const username = form.elements.username;
const email = form.elements.email;
const password = form.elements.password;
// Валидация имени пользователя
if (username.value.trim() === '') {
displayError(username, 'Username is required');
isValid = false;
} else if (username.value.length < 3) {
displayError(username, 'Username must be at least 3 characters');
isValid = false;
}
// Валидация email
if (email.value.trim() === '') {
displayError(email, 'Email is required');
isValid = false;
} else if (!isValidEmail(email.value)) {
displayError(email, 'Please enter a valid email address');
isValid = false;
}
// Валидация пароля
if (password.value === '') {
displayError(password, 'Password is required');
isValid = false;
} else if (password.value.length < 8) {
displayError(password, 'Password must be at least 8 characters');
isValid = false;
} else if (!/[A-Z]/.test(password.value) || !/[a-z]/.test(password.value) || !/[0-9]/.test(password.value)) {
displayError(password, 'Password must include uppercase, lowercase, and numbers');
isValid = false;
}
return isValid;
}
function setupRealTimeValidation() {
const inputs = form.querySelectorAll('input');
inputs.forEach(input => {
input.addEventListener('input', function() {
// Очищаем ошибку, когда пользователь начинает печатать
const errorElement = this.parentElement.querySelector('.error-message');
if (errorElement) {
errorElement.remove();
this.classList.remove('error-input');
}
});
});
// Специфичная валидация email
form.elements.email.addEventListener('blur', function() {
if (this.value && !isValidEmail(this.value)) {
displayError(this, 'Please enter a valid email address');
}
});
}
// Вспомогательные функции
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function displayError(input, message) {
const formControl = input.parentElement;
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.textContent = message;
formControl.appendChild(errorElement);
input.classList.add('error-input');
}
function clearErrors() {
document.querySelectorAll('.error-message').forEach(error => error.remove());
document.querySelectorAll('.error-input').forEach(input => {
input.classList.remove('error-input');
});
}
function showSuccessMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = 'success-message';
messageElement.textContent = message;
form.parentElement.insertBefore(messageElement, form);
// Удаляем через 3 секунды
setTimeout(() => {
messageElement.remove();
}, 3000);
}
function showErrorMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = 'error-banner';
messageElement.textContent = message;
form.parentElement.insertBefore(messageElement, form);
// Удаляем через 3 секунды
setTimeout(() => {
messageElement.remove();
}, 3000);
}
// Имитируем отправку API
function submitFormData(data) {
return new Promise((resolve, reject) => {
// Имитируем сетевой запрос
setTimeout(() => {
// 90% успешности для демонстрации
if (Math.random() > 0.1) {
resolve({ success: true, message: 'User created' });
} else {
reject({ success: false, message: 'Server error' });
}
}, 1500);
});
}
});
Соображения доступности форм
При обработке форм с помощью JavaScript убедитесь, что они остаются доступными:
- Управление фокусом: При отображении ошибок устанавливайте фокус на первое невалидное поле
- Используйте ARIA атрибуты: Добавляйте
aria-invalid="true"
к невалидным полям - Связывайте сообщения об ошибках: Используйте
aria-describedby
для связи полей с их сообщениями об ошибках - Предоставляйте четкие инструкции: Используйте элементы
<label>
и описательные сообщения об ошибках - Поддерживайте навигацию с клавиатуры: Убедитесь, что все элементы управления формы доступны с клавиатуры
Пример доступной обработки ошибок:
function displayError(input, message) {
const formControl = input.parentElement;
const errorId = `${input.id}-error`;
const errorElement = document.createElement('div');
errorElement.id = errorId;
errorElement.className = 'error-message';
errorElement.textContent = message;
formControl.appendChild(errorElement);
// Добавляем атрибуты доступности
input.setAttribute('aria-invalid', 'true');
input.setAttribute('aria-describedby', errorId);
// Устанавливаем фокус на первое невалидное поле
if (!document.querySelector('.error-input')) {
input.focus();
}
input.classList.add('error-input');
}
Часто задаваемые вопросы
Используйте встроенный метод reset для элемента формы: document.getElementById('myForm').reset();
Используйте метод checkValidity Constraint Validation API: const isValid = form.checkValidity();
Для асинхронной отправки данных формы используйте Fetch API вместе с объектом FormData. Сначала создайте экземпляр FormData из вашей формы. Затем отправьте его с помощью fetch с методом POST. Обязательно обработайте ответ и перехватите любые ошибки во время отправки.
Для обработки загрузки файлов получите доступ к свойству files элемента file input, получите выбранный файл и добавьте его в объект FormData перед отправкой с помощью fetch или другого метода.
Событие `input` срабатывает всякий раз, когда изменяется значение поля, в то время как событие `change` срабатывает только когда поле теряет фокус и значение изменилось с момента получения фокуса.
Для динамического добавления полей формы создайте новые элементы input с помощью document.createElement, установите их тип и имя, и добавьте их в контейнерный элемент в форме.
Заключение
Овладев обработкой форм на чистом JavaScript, вы получаете полный контроль над своими формами без зависимости от внешних библиотек. Этот подход уменьшает зависимости, улучшает производительность и углубляет ваше понимание того, как на самом деле работают веб-формы.