12k
All articles

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

Статья показывает, как перехватывать отправку форм, валидировать данные через HTML5-ограничения и считывать значения полей с помощью FormData API.

OpenReplay Team
OpenReplay Team
Обработка форм на чистом 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');
}

Часто задаваемые вопросы

Как сбросить форму с помощью JavaScript?

Используйте встроенный метод reset для элемента формы: document.getElementById('myForm').reset();

Как проверить, валидна ли форма перед отправкой?

Используйте метод checkValidity Constraint Validation API: const isValid = form.checkValidity();

Как отправить данные формы асинхронно?

Для асинхронной отправки данных формы используйте Fetch API вместе с объектом FormData. Сначала создайте экземпляр FormData из вашей формы. Затем отправьте его с помощью fetch с методом POST. Обязательно обработайте ответ и перехватите любые ошибки во время отправки.

Как обрабатывать загрузку файлов с помощью чистого JavaScript?

Для обработки загрузки файлов получите доступ к свойству files элемента file input, получите выбранный файл и добавьте его в объект FormData перед отправкой с помощью fetch или другого метода.

В чем разница между событиями input и change?

Событие `input` срабатывает всякий раз, когда изменяется значение поля, в то время как событие `change` срабатывает только когда поле теряет фокус и значение изменилось с момента получения фокуса.

Как динамически добавлять поля формы с помощью JavaScript?

Для динамического добавления полей формы создайте новые элементы 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

We use cookies to improve your experience. By using our site, you accept cookies.