Back

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

Обработка форм на чистом 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": Валидация формата email
  • type="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 убедитесь, что они остаются доступными:

  1. Управление фокусом: При отображении ошибок устанавливайте фокус на первое невалидное поле
  2. Используйте ARIA атрибуты: Добавляйте aria-invalid="true" к невалидным полям
  3. Связывайте сообщения об ошибках: Используйте aria-describedby для связи полей с их сообщениями об ошибках
  4. Предоставляйте четкие инструкции: Используйте элементы <label> и описательные сообщения об ошибках
  5. Поддерживайте навигацию с клавиатуры: Убедитесь, что все элементы управления формы доступны с клавиатуры

Пример доступной обработки ошибок:

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, вы получаете полный контроль над своими формами без зависимости от внешних библиотек. Этот подход уменьшает зависимости, улучшает производительность и углубляет ваше понимание того, как на самом деле работают веб-формы.

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers