Как сделать обработчик формы

Обновлено: 04.07.2024

Помимо стандартных средств работы с формами можно использовать JavaScript, чтобы проверять формы на валидность, получать доступ к значениям и отправлять информацию на сервер.

Трюки для работы с формами в JS проще всего показать на примере. В этой статье мы соберём форму заявки на участие в миссии по колонизации Марса. В этой форме мы немножко приправим стандартные HTML-атрибуты динамикой на JS.

Разметка и требования

  • Имя, чтобы знать, как обращаться в ответном письме.
  • Почту, чтобы знать, куда это письмо слать.
  • Возраст — нужны только молодые 🤷‍♂️
  • Специализацию — инженеры и учёные пригодятся для основной работы, а психологи нужны, чтобы команда друг друга не перегрызла за десятилетнюю колонизаторскую миссию.
  • Работал ли человек в NASA — это большой плюс.
  • Фотография, чтобы использовать в печатных материалах.

В целом форма рабочая: обязательные поля не пропустят пустые значения, атрибут type проследит, чтобы вместо почты нам не прислали номер телефона, а по нажатию на кнопку валидная форма отправит все данные.

Но нам кроме всего этого хочется:

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

Отправка без перезагрузки

Первым делом настроим отправку формы без перезагрузки страницы.

Предотвращаем отправку данных

Если наше событие находится в переменной event , то для предотвращения поведения по умолчанию мы можем вызвать event.preventDefault() .

Мы можем просто передать функцию handleFormSubmit как второй аргумент в addEventListener , так как он автоматически передаст событие в качестве аргумента для handleFormSubmit .

Получится, что при отправке формы сработает событие submit , которое запустит наш обработчик handleFormSubmit .

В этот обработчик как аргумент event будет передано событие отправки. Мы вызовем event.preventDefault() , и форма не отправится самостоятельно.

Собираем данные из формы

Нам не хочется собирать каждое значение отдельно.

  • Это может быть долго: если форма состоит из 10 полей, это уже требует достаточно много кода.
  • Это не масштабируется: если мы захотим добавить ещё пару полей, нам придётся писать код и для этих полей тоже.

Вместо этого мы будем использовать возможности языка, чтобы достать все поля и элементы управления из формы. Напишем функцию serializeForm :

Аргумент функции serializeForm — это элемент формы. Именно элемент — не селектор, а конкретный узел в DOM-дереве.

У форм есть свойство elements , которое содержит в себе все элементы управления и поля этой формы. Именно этим свойством мы воспользуемся, чтобы получить все данные из формы.

Если сейчас мы вызовем эту функцию, передав туда нашу форму как аргумент, то в консоли появится список всех элементов:

Обратите внимание, что тип этого набора элементов — HTMLFormControlsCollection . Это не массив и, чтобы пройтись циклом по списку элементов, нужно превратить его в массив с помощью вызова Array.from() .

Нам останется собрать имя и значение каждого из полей. Для начала, выведем имя и значение каждого элемента в консоль:

Мы получили список элементов, преобразовали его в массив и прошлись по каждому элементу. У каждого элемента получили поля name и value и вывели их в консоль.

В консоли после запуска получим вывод по каждому из полей:

Заметим, что последняя строчка не имеет ни названия, ни значения. Это потому, что последний элемент, который мы проверяли — это кнопка.

Чтобы элементы без названия нам не мешались, мы отфильтруем наш набор. Воспользуемся методом filter , чтобы отбросить элементы с пустым именем. Также заменим метод forEach на map — он соберёт нам массив, который хранит объект с именем и значением каждого отфильтрованного элемента.

На выходе в консоли получится массив из объектов с name и value :

Значения чекбоксов

Сейчас можно заметить, что nasa-experience имеет значение "1" . Это неправильно:

  • мы не отмечали чекбокс, а значение почему-то "1" ;
  • в целом хотелось бы, чтобы значение этого поля было булевым.

Для этого мы можем использовать особое свойство checked , которое есть у чекбоксов.

Значение этого поля как раз булево, и мы можем использовать это в нашей функции serializeForm .

Но это свойство мы хотим использовать только на чекбоксе, а не на остальных полях. Это тоже можно сделать. Прочитаем тип элемента и, если он "checkbox" , то возьмём в качестве значения поле checked :

Теперь значение поля nasa-experience будет true , если чекбокс отмечен, и false , если пропущен. Увидим такой вывод:

Формат данных

В целом, нынешний формат данных в виде массива объектов нам может и подойти, но мы с вами используем кое-что лучше — FormData .

FormData — это особый тип данных, который можно использовать для отправки данных формы на сервер.

Мы воспользуемся им, чтобы сохранить данные из формы. Создадим экземпляр с помощью new FormData() , откажемся от массива со значениями и будем добавлять имена полей и их значения в FormData с помощью вызова функции append :

Но так как тип FormData специально создан для работы с формами, можно сделать гораздо проще 🙂

Стоит отметить, что nasa-experience в таком случае попадёт в финальные данные, только если чекбокс отметили. Если его не отметить, то в финальных данных он не окажется.

Когда чекбокс nasa-experience выделен, получим такой вывод:

Когда чекбокс не выделен — такой:

В первом случае чекбокс был отмечен, поэтому в списке есть элемент nasa-experience , во втором случае чекбокс был пропущен, поэтому такого элемента в списке данных нет.

Чтобы проверить, какие данные в себе содержит переменная типа FormData , можно использовать метод .entries() , он выведет список с данными, как в примере выше.

Отправка на сервер

Функция будет асинхронной, потому что работает с сетевыми запросами. В качестве аргумента она принимает FormData и отправляет запрос с помощью вызова fetch . Нам нужно указать правильный заголовок Content-Type у запроса, для формы он 'multipart/form-data' :

Функция вернёт результат запроса к серверу, который мы сможем проверить на ошибки.

Теперь используем эту функцию в обработчике события отправки. Сериализуем форму и передадим её в функцию отправки. Вместо обращения напрямую к форме, будем читать её из объекта события. Форма в объекте события submit будет храниться в свойстве target :

Начнём с лоадера.

Показываем лоадер во время отправки

Добавим его после кнопки и спрячем:

Прячем мы его, потому что хотим показать только во время запроса. Для этого напишем функцию, которые будут управлять его состоянием — делать лоадер видимым, если он не виден сейчас, и скрывать, если он виден. Так как технически это добавление и удаление класса hidden , то можно воспользоваться функцией toggle из classList API:

Вызовем эту функцию до отправки запроса, чтобы показать лоадер, и после запроса, чтобы скрыть. Лоадер будет виден до тех пор, пока запрос не завершится:

Обрабатываем успешную отправку

Обрабатываем ошибки

Мы могли бы вызвать alert сразу на месте, но лучше вынести обработку ошибки в отдельную функцию. Так, если нам захочется добавить какие-то действия по обработке ошибок, нам будет проще ориентироваться в коде.

Если что-то пошло не так, мы увидим причину. Форма останется на месте.

Блокируем кнопку отправки на невалидной форме

Давайте будем её блокировать до тех пор, пока не будут заполнены все поля, которые требуется заполнить.

Напишем функцию, которая будет проверять валидность формы и блокировать кнопку, если требуется. Аргументом она будет принимать событие ввода с клавиатуры на полях ввода.

Так как событие ввода будет происходить на полях, а не на самой форме, то значение event.target — это поле. Чтобы получить форму, воспользуемся свойством form , значением которого является ссылка на родительскую форму.

Проверять валидность формы будем с помощью метода checkValidity() формы. Он запускает стандартные проверки. Результат проверки будем использовать для того чтобы установить свойство disabled кнопки в значение true , если нужно заблокировать, и false , если кнопка должна быть доступна.

Скриншот заблокированной кнопки отправки

Теперь, пока форма не будет заполнена, кнопка будет заблокирована.

Что у нас получилось

Итоговый вид формы

Для всего этого мы использовали методы HTML-элементов и элементов форм, которые нам предоставляет браузер и веб-платформа.

Конечно, этим работа с формами не заканчивается. Ещё можно сделать валидацию каждого поля в отдельности, загрузку картинок с возможностью их редактирования, добавить всякие комбо-боксы и нестандартные элементы.

Теперь разъяснения этого кода. Думаю на счет таких тегов как , , и так все понятно, так что перейдем сразу к форме.

Здесь мы пишем тег

Набросаем класс формы. Он должен уметь:

  • Работать в своём нэймспэйсе, пространстве имён, чтобы на одной странице можно было разместить несколько форм…

Как это можно организовать с помощью php ?

Дело в том, что если на странице несколько одинаковых форм, то при нажатии кнопки submit произойдет что-то невнятное. Поэтому формы надо уметь различать. Это делается средствами html — каждой форме (ее кнопкам и полям ввода) дается свой уникальный id или можно дать форме имя (поле name).

Код на php генерирует этот самый html. В конце заметки приводится этот код, я приведу еще раз его кусочек:

Думаю можно, но мое мнение в этом вопросе не очень компетентное, я почти не занимаюсь вебом.

В этом уроке мы рассмотрим примеры безопасной обработки запросов в PHP.

Общие сценарии с обработкой GET- и POST-запросов мы рассмотрели в соответствующих уроках курса PHP для начинающих. Однако, несмотря на приведенные в уроках примеры, многие начинающие продолжают делать ошибки, никак не проверяя данные, пришедшие с веб-формы. Поэтому я решил сделать отдельный урок по этой теме, чтобы было понимание, как делать можно, а как - нельзя.

Давайте в качестве примера рассмотрим одно из домашних заданий, выполненных учеником к уроку Калькулятор на PHP.

Вот непосредственно форма для отправки запроса:

Вот скрипт result.php, принимающий запрос:

А вот скрипт calc.php, в котором происходит обработка запроса:


После чего жмём кнопку "Посчитать".


На первый взгляд всё работает. Давайте проверим как работает валидация того, что поле заполнено. Оставляем один из аргументов пустым.


И отправляем запрос.

Отлично! Всё прекрасно обработалось.


Давайте теперь в качестве аргумента попробуем передать не числовое значение.


И снова всё прекрасно:


То получим следующие ошибки:

Если в данный момент ошибки у вас не отобразились, нужно в php.ini вашего development-окружения задать следующие директивы:

После чего перезапустить веб-сервер.


И если вы думаете, что никто так делать не будет, то вы ошибаетесь. Как только вы выложите сайт в интернет, его будут пытаться взломать и будут слать в запросах абсолютно дичайшую дичь.

Что же нам делать? Нам в самом простом случае просто требуется проверить, что такое поле в массиве $_GET есть. Сделать это можно с помощью empty:


Однако, если передать 0 в качестве одного из аргументов, наш калькулятор поведет себя некорректно:

Всё потому, что empty(0) вернёт true. Вместо этого можно воспользоваться конструкцией isset:


В таком случае всё будет корректно валидироваться.


С аналогичным успехом можно было воспользоваться функцией array_key_exists:

С той лишь разницей, что если в массиве по нужному нам ключу будет null, то isset для него вернёт false. То есть разница в следующем:

Это нужно учитывать, если в запросе в качестве валидного значения может прилететь null.

А ещё в PHP 7 появился null-coalesce оператор, который очень удобно использовать для наших целей:

И далее по коду уже работать с переменными $x1 и $x2.

После того, как мы проверили существование ключа, нужно проверить что значение по этому ключу является валидным. В нашем случае требуется проверить, что x1 и x2 - числа. Для этого можно использовать функцию is_numeric:

А дальше, если уж всё совсем делать правильно, требуется привести значения переменных к правильному типу, ведь значения, пришедшие с формы всегда строковые.

И только после этого можно приступать к работе с этими значениями.

В PHP также есть специальные функции, позволяющие упростить обработку входных значений. Одна из таких функций - filter_input. Ознакомиться с ней я предлагаю вам самостоятельно в рамках домашнего задания к этому уроку. В качестве самостоятельной работы предлагаю вам переписать код обработчика, приведенного ниже, с использованием этой функции:

Когда я впервые начал изучать потенциал статических сайтов, меня привлекла их скорость и простота. Тем не менее, я знал, что эти преимущества имеют свою цену: поскольку статические сайты не могут запускать бэкэнд-код, существуют ограничения относительно того, что вы можете выполнить с помощью статического решения. Больше таких ограничений я не вижу.

JAMStack позволяет не жертвовать функциями ради производительности, с его помощью легко поддерживать сайт. Скорее, речь идет о переоценке того, какая часть ваших функций должна быть делегирована веб-интерфейсу. Например, должно ли то же программное обеспечение, которое отображает контактную HTML-форму, обрабатывать представление из этой формы данных?

Представленные здесь решения представляют весь спектр вариантов от полностью управляемых решений до самообслуживаемых альтернатив для тех, кто хочет иметь полный контроль.

Google Forms

5 способов обработки форм на статическом сайте


Практический курс по верстке адаптивного сайта с нуля!

Изучите курс и узнайте, как верстать современные сайты на HTML5 и CSS3

Основное преимущество: простой в использовании конструктор форм. Основной недостаток: не интегрируется с сайтом. Невозможно модифицировать существующие формы, необходимо повторно создать их в Google Forms.

Защита от спама

Google реализует reCAPTCHA для форм, которые запрашивают адреса электронной почты.

Расширяемость

У Google есть полезная библиотека адд-онов для форм, вы также можете добавлять функционал в формы с помощью Google Apps Scripts. Ответы легко экспортировать в таблицы Google для дальнейшей обработки.

Использование Google Forms

FormKeep

5 способов обработки форм на статическом сайте

FormKeep — это еще решение SaaS для обработки форм. FormKeep легко интегрируется с существующими формами, вам только нужно изменить атрибут action в HTML-форме. Вы можете просматривать предоставленные через форму данные в панели инструментов FormKeep.

Основное преимущество: множество вариантов интеграции. Основной недостаток: нет бесплатного плана, только ограниченные ознакомительные версии.

Защита от спама

FormKeep использует стратегию фильтрации для автоматического обнаружения спама. Вы также можете добавить в формы поле reCAPTCHA. Дополнительные материалы: Как FormKeep защищает от спама?

Расширяемость

FormKeep позволяет добавлять веб-хуки, которые будут вызываться при получении данных, и интегрируется с несколькими сервисами, включая Zapier.

Использование FormKeep

Netlify Forms

5 способов обработки форм на статическом сайте

Netlify предлагает уникальный подход к интеграции форм на сайт. JavaScript не требуется, ваша форма не публикуется на другом домене: вы просто добавляете атрибут netlify в тег


Практический курс по верстке адаптивного сайта с нуля!

Изучите курс и узнайте, как верстать современные сайты на HTML5 и CSS3

Formspree

5 способов обработки форм на статическом сайте

Formspree также является проектом с открытым исходным кодом (см. на Github) и содержит инструкции относительно того, как запустить собственный экземпляр Formspree на Heroku.

Основное преимущество: открытый исходный код, может запускать собственный экземпляр на Heroku. Основной недостаток: отправленные данные остаются только в электронной почте (бесплатно).

Защита от спама

Formspree использует для выявления спама reCAPTCHA. Вы также можете добавить в форму поле honeypot с именем _gotcha.

Расширяемость

Formspree не предлагает возможности расширения из коробки.

Использование Formspree

Вы можете начать использовать Formspree, даже не посещая их веб-сайт. Просто укажите для своей форме //formspree.io/$YOUR_EMAIL, где $YOUR_EMAIL — это электронный адрес, на который вы хотите получать отправленные ответы. После корректной настройки формы заполните ее один раз. Затем вы получите электронное письмо от Formspree с просьбой подтвердить свой адрес электронной почты.

Если вы используете одну и ту же форму на нескольких URL-адресах, вам нужно выполнить подтверждение для каждого URL-адреса. Ниже показано, как добавить поля honeypot.

FormPlug

5 способов обработки форм на статическом сайте

FormPlug — это вариант для опытных пользователей, которым нужен полный контроль над кодом, который обрабатывает отправленные данные. FormPlug — это проект с открытым исходным кодом, созданный с использованием Serverless Framework. Код работает на AWS Lambda и использует для отправки электронной почты SAS Amazon.

Защита от спама

FormPlug также предлагает поле honeypot с именем _honeypot. По моему опыту, поля honeypot достаточно эффективны сами по себе. Тем не менее, они не кажутся очень надежными, и я не удивлюсь, если в какой-то момент они станут совершенно бесполезными. FormPlug также позволяет шифровать адрес электронной почты, чтобы он не отображался на интерфейсе.

Расширяемость

Подобно Formspree, FormPlug не предлагает расширяемости из коробки. Поскольку это проект с открытым исходным кодом, у вас всегда есть возможность расширить его и, по крайней мере, реализовать собственное расширение.

Использование FormPlug

Чтобы использовать FormPlug, вам понадобится учетная запись AWS. Следуйте инструкциям по установке, чтобы установить FormPlug в среде AWS.

Читайте также: