События формы: submit, input, change

submit срабатывает при отправке формы, input — при каждом изменении значения поля, change — после завершения изменения и потери фокуса; каждое используется в своём сценарии взаимодействия с формой.

Зачем нужно

Правильный выбор между input и change влияет на производительность и UX: input нужен для мгновенной реакции (живой поиск, подсчёт символов), change — для более «тяжёлых» операций после завершения ввода. submit — единственный надёжный способ перехватить отправку формы.

Где используется

  • submit — отправка формы через AJAX без перезагрузки страницы
  • input — живой поиск, подсчёт символов, валидация в реальном времени
  • change — изменение настроек, смена значений <select>, <checkbox>, файловый инпут

submit

const form = document.querySelector('#login-form');

form.addEventListener('submit', async (event) => {
  event.preventDefault(); // обязательно — отменяем reload

  const formData = new FormData(form);
  const payload  = Object.fromEntries(formData);

  try {
    const res = await fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    if (!res.ok) throw new Error('Ошибка авторизации');
    const { token } = await res.json();
    localStorage.setItem('token', token);
    location.assign('/dashboard');
  } catch (err) {
    showFormError(err.message);
  }
});

input

Срабатывает при каждом изменении значения (каждый нажатый символ):

const search = document.querySelector('#search');
const results = document.querySelector('#results');

search.addEventListener('input', async (event) => {
  const query = event.target.value.trim();
  if (query.length < 2) {
    results.innerHTML = '';
    return;
  }
  const data = await fetchSearch(query);
  renderResults(data);
});

// Счётчик символов
const textarea = document.querySelector('#bio');
const counter  = document.querySelector('#char-count');

textarea.addEventListener('input', () => {
  counter.textContent = `${textarea.value.length} / 300`;
  if (textarea.value.length > 300) counter.classList.add('over');
});

change

Срабатывает после завершения изменения и потери фокуса (или сразу при выборе для checkbox/select):

// Select — мгновенно при выборе
const countrySelect = document.querySelector('#country');
countrySelect.addEventListener('change', (e) => {
  loadCities(e.target.value); // загружаем города для выбранной страны
});

// Checkbox — мгновенно
const darkMode = document.querySelector('#dark-mode');
darkMode.addEventListener('change', () => {
  document.body.classList.toggle('dark', darkMode.checked);
  localStorage.setItem('darkMode', darkMode.checked);
});

// Text input — после потери фокуса (blur)
const username = document.querySelector('#username');
username.addEventListener('change', async () => {
  const available = await checkUsernameAvailability(username.value);
  showAvailability(available);
});

input vs change для text-полей

const field = document.querySelector('#price');

// input: каждый символ — подходит для мгновенного отклика
field.addEventListener('input', () => {
  previewTotal(field.value); // лёгкая операция
});

// change: только после blur — подходит для тяжёлых операций
field.addEventListener('change', () => {
  validateAndSave(field.value); // тяжёлая операция
});

Файловый инпут

const fileInput = document.querySelector('#avatar');

fileInput.addEventListener('change', () => {
  const file = fileInput.files[0];
  if (!file) return;

  if (file.size > 5 * 1024 * 1024) {
    alert('Файл слишком большой (макс. 5 МБ)');
    fileInput.value = ''; // очищаем
    return;
  }

  const reader = new FileReader();
  reader.onload = (e) => {
    preview.src = e.target.result;
  };
  reader.readAsDataURL(file);
});

Частые ошибки

1. Забытый preventDefault в submit

form.addEventListener('submit', (e) => {
  // Без e.preventDefault() — страница перезагрузится
  processForm; // может не выполниться
});

2. Использование change для живого поиска

// Плохо: change срабатывает только после blur
search.addEventListener('change', performSearch); // пользователь печатает и ничего не происходит

// Хорошо: input для мгновенной реакции
search.addEventListener('input', performSearch);

3. Многократная отправка формы

// Защита от двойного клика
form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const submitBtn = form.querySelector('[type="submit"]');
  submitBtn.disabled = true;
  try {
    await sendData;
  } finally {
    submitBtn.disabled = false; // восстанавливаем в любом случае
  }
});

Связанные темы

Ресурсы