Можно ли повесить обработчик сабмита на кнопку и на форму одновременно
Что проверяют
Понимаете ли вы, где на самом деле возникает событие submit и чем оно отличается от клика по кнопке. Формулировка провоцирует ответить «да, конечно», хотя подписаться на сабмит у кнопки бессмысленно.
Как отвечать
Событие submit возникает на элементе form, а не на кнопке. Повесить обработчик submit на кнопку синтаксически можно, но он не вызовется никогда: события всплывают вверх, а не вниз, а целью сабмита всегда остаётся форма.
Реальная пара выглядит иначе: click на кнопке и submit на форме. Порядок такой: браузер сначала отдаёт клик, он всплывает от кнопки до формы и выше, затем запускает встроенную валидацию полей, и только если она прошла, порождает сабмит на форме.
form.addEventListener("submit", (e) => {
e.preventDefault();
send(new FormData(form));
});
button.addEventListener("click", () => {
// сработает раньше submit, валидация ещё не проходила
});Отсюда практический вывод: отправку данных вешают на submit формы, а на клик кнопки оставляют только то, что относится к самой кнопке. Форму отправляют не одной мышью: Enter в текстовом поле и вызов form.requestSubmit() дают сабмит вообще без клика.
Где спотыкаются
Складывают всю логику в обработчик клика. Отправка по Enter и программный вызов проходят мимо него, и часть пользователей отправляет форму без вашей проверки.
Второе: зовут form.submit() из кода и ждут, что обработчик сработает. Этот метод отправляет форму напрямую, не порождая события и не запуская валидацию. Нужен form.requestSubmit().
Третье: путают всплытие с действием по умолчанию. Вызов stopPropagation() в клике отправку не отменит, её отменяет только preventDefault(). И наоборот, preventDefault() в обработчике клика отменяет отправку целиком, так что до формы сабмит уже не доедет.