Отправка формы и событие submit
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Форма — единственный элемент страницы, который умеет уйти на сервер сам, без единой строки JavaScript. Поэтому работа с формами начинается не с вопроса «как отправить», а с вопроса «как вовремя перехватить»: событие submit — та самая точка, где браузер спрашивает разрешения.
Форму отправляет не кнопка, а форма. Кнопка только нажимается — дальше браузер сам решает, что клик по submit-кнопке означает отправку, и возбуждает событие submit уже на самом элементе <form>. Обработчик клика и обработчик отправки — это две разные точки в одной цепочке, и порядок у них строгий: сначала click, потом submit.
Разница не косметическая. К отправке ведёт несколько дорог: клик по кнопке, Enter в текстовом поле, вызов метода из кода, вторая submit-кнопка рядом. Обработчик на click ловит ровно одну из них, обработчик на submit — все сразу. Поэтому правило простое: слушаем форму.
Это называется неявной отправкой. Если в форме есть submit-кнопка, Enter в однострочном поле как бы нажимает её — и дальше всё идёт обычным путём: click, затем submit. Если submit-кнопки нет вообще, а поле одно, форма всё равно отправится, но event.submitter окажется null.
Из кода такую отправку не воспроизвести: синтетический Enter, отправленный через dispatchEvent, неявную отправку не запускает — браузер выполняет её только для настоящего ввода. Для тестов и примеров используйте requestSubmit().
Действие по умолчанию у submit — навигация: браузер собирает значения полей, уходит по адресу из action, и страница перезагружается. event.preventDefault() отменяет именно это. Всё остальное остаётся при вас: данные в полях, состояние страницы, возможность отправить их так, как вы захотите.
Отмена ничего не ломает задним числом. Встроенная валидация к моменту вызова обработчика уже прошла — иначе события бы просто не было. И всплытие тоже продолжается: preventDefault отменяет действие браузера, а не путь события по дереву.
FormData — готовое тело запроса: его можно передать в fetch как есть, браузер сам расставит границы полей и заголовок. Здесь мы только объявляем функцию (сети в песочнице нет), но в настоящем приложении дальше идёт обычный await и разбор ответа.
Иногда отправку запускает код: пользователь выбрал вариант в списке, истёк таймер, завершилась загрузка файла. Методов два, и разница между ними принципиальная. form.submit() отправляет молча — ни события submit, ни проверки required. form.requestSubmit() ведёт себя как настоящий клик по кнопке: сначала валидация, потом событие, и только потом отправка.
Практический вывод: в коде приложения почти всегда нужен requestSubmit(). Он не обходит вашу же логику — если обработчик вызовет preventDefault(), отправка остановится, как и должна.
| Способ отправки | Событие submit | Встроенная валидация |
|---|---|---|
| Клик по кнопке type=submit | есть | есть |
| Enter в текстовом поле | есть | есть |
| form.requestSubmit() | есть | есть |
| form.requestSubmit(кнопка) | есть, submitter — эта кнопка | есть |
| form.submit() | нет | нет |
form.submit() — дверь без звонка: страница уходит, а обработчик submit об этом не узнает. Если где-то в проекте остался такой вызов, вешать на submit единственную проверку данных или отправку аналитики уже нельзя.
В форме может быть несколько submit-кнопок: «Сохранить» и «Удалить», «Опубликовать» и «В черновики». Событие у них общее, поэтому обработчику нужен способ узнать, кто именно его вызвал. Это event.submitter — ссылка на нажатую кнопку.
Если у кнопки есть name и value, они попадают и в данные формы — но только если сказать FormData, кто был инициатором: второй аргумент конструктора как раз для этого. Без него пара кнопки в набор не войдёт.
Внутри формы кнопка без атрибута type — это type="submit". Не «ничего», не «просто кнопка», а полноценная отправка. Отсюда классический баг: рядом с «Сохранить» ставят «Показать пароль» или «Добавить строку», и страница на каждый клик моргает и обнуляется.
Значений три: submit (по умолчанию) отправляет форму, reset возвращает поля к исходным значениям, button не делает ничего — вся её работа в вашем обработчике. Для любой кнопки, которая не отправляет форму, type="button" обязателен: это дешевле и честнее, чем гасить последствия через preventDefault.
- Отправку ловят на форме: событие submit собирает все пути сразу — клик по submit-кнопке, Enter в поле, вызов из кода.
- event.preventDefault() отменяет только навигацию: данные, валидация и всплытие события остаются нетронутыми.
- form.requestSubmit() — честный клик: валидация и событие submit. form.submit() отправляет молча, минуя и то, и другое.
- event.submitter показывает нажатую кнопку, а new FormData(form, submitter) добавляет её name и value в данные.
- Кнопка без type внутри формы отправляет форму. Всё, что не отправка, помечается type="button".
1/3Внутри формы стоит кнопка без атрибута type. Что произойдёт при клике по ней?
2/3Чем form.submit() отличается от form.requestSubmit()?
3/3Обработчик submit вызвал event.preventDefault(). Что именно отменено?
Своя отправка вместо перезагрузки
Форма поиска должна работать без перезагрузки. Повесьте обработчик на форму, отмените стандартную отправку и выведите значение поля query и число полей в наборе данных. Проверьте себя вызовом requestSubmit().
Слушаем форму, а не кнопку: тогда обработчик сработает и на клик, и на Enter, и на программную отправку.
Кнопка в набор не попала: у неё нет атрибута name, а безымянные поля FormData игнорирует. Дальше этот же объект можно отдать в fetch как тело запроса.
Кнопка, которая не должна отправлять
Кнопка «Очистить» должна только опустошать поле, но вместе с этим каждый раз отправляется форма — счётчик submits растёт. Найдите причину и почините разметкой, а не обходным путём.
Причина в умолчании: кнопка без type внутри формы — это submit-кнопка, поэтому после вашего обработчика клика браузер отправляет форму. Достаточно объявить намерение в разметке.
После этого submits остаётся 0, а обработчик клика не меняется. Вариант с event.preventDefault() внутри обработчика кнопки тоже сработает, но разметка продолжит врать: кнопка объявлена отправляющей, а поведение переопределено скриптом — и следующий человек починит это заново.