Как структурировать HTML-формы - Изучение веб-разработки

Как структурировать HTML-формы
==============================

* Назад
* Обзор: Forms
* Далее

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

| Уровень подготовки: | Основы компьютерной грамотности, и базовые знания HTML. |
| --- | --- |
| Цель: | Разобраться как структурировать HTML формы и задавать им семантику для того, чтобы они были удобны и доступны в использовании. |

Гибкость HTML форм делает их одной из самых сложных структур в HTML; вы можете создать любую форму, используя элементы и атрибуты форм. Использование правильной структуры, при создании HTML форм, поможет гарантировать их удобство и доступность.

Элемент <form>
--------------

Элемент `<form>` формально определяет форму и атрибуты, которые определяют поведение этой формы. Каждый раз, когда вы хотите создать HTML-форму, вам нужно начать с создания элемента `<form>`, поместив внутрь него всё содержимое. Многие вспомогательные технологии или браузерные плагины могут обнаруживать элементы `<form>` и реализовывать специальные хуки, чтобы их было проще использовать.

Мы уже встречались с этим в предыдущей статье.

**Предупреждение:** **Внимание:** Строго запрещается размещать форму внутри другой формы. Такое размещение может привести к непредсказуемому поведению форм, в зависимости от браузера.

Стоит учесть, что всегда можно использовать элементы формы вне `<form>`. Тогда по умолчанию этот элемент формы не имеет ничего общего со всеми формами. Вы можете связать его с формой с помощью атрибута `form`. В HTML5 был представлен атрибут `form` для элементов HTML форм, который позволяет явно связать элемент с формой, даже если он не заключён внутри `<form>`.

Элементы <fieldset> и <legend>
------------------------------

Элемент `<fieldset>` - это удобный способ стилистической и семантической группировки элементов формы. Вы можете установить заголовок `<fieldset>`, добавив элемент `<legend>` сразу после открывающего тега `<fieldset>`. Текст элемента `<legend>` формально описывает назначение содержимого `<fieldset>`.

Различные вспомогательные технологии будут использовать `<legend>` как часть метки `label` всех элементов внутри `<fieldset>`. Например, такие экранные дикторы как Jaws или NVDA произносят заголовок формы `<legend>` перед произношением названия меток элементов.

Небольшой пример:

```html
<form>
  <fieldset>
    <legend>Fruit juice size</legend>
    <p>
      <input type="radio" name="size" id="size\_1" value="small" />
      <label for="size\_1">Small</label>
    </p>
    <p>
      <input type="radio" name="size" id="size\_2" value="medium" />
      <label for="size\_2">Medium</label>
    </p>
    <p>
      <input type="radio" name="size" id="size\_3" value="large" />
      <label for="size\_3">Large</label>
    </p>
  </fieldset>
</form>
```

**Примечание:** вы можете найти этот пример в fieldset-legend.html (также посмотрите на результат).

Читая эту форму, экранный диктор произнесёт "Fruit juice size small" для первого элемента, "Fruit juice size medium" - для второго, "Fruit juice size large" - для третьего.

Вариант использования в этом примере является одним из наиболее важных. Каждый раз, когда у вас есть набор переключателей, вам нужно поместить их внутри `<fieldset>`. Также `<fieldset>` можно использовать для разделения формы. В идеале, длинную форму разделяют на несколько страниц, однако, если она должна находиться на одной странице, распределение связанных элементов в разные `<fieldset>` может повысить удобство использования.

Из-за своего влияния на вспомогательные технологии элемент `<fieldset>` является одним из ключевых элементов для построения доступных форм; однако вы не должны им злоупотреблять. Если возможно, старайтесь проверять, как экранный диктор интерпретирует вашу форму.

Элемент <label>
---------------

В предыдущей статье мы увидели, что элемент `<label>` принято использовать для указания текстов-подсказок (лейблов) в HTML-формах. Это самый важный элемент для построения доступных форм — при правильной реализации скринридеры будут озвучивать текст-подсказку вместе со связанными элементами. Посмотрите на этот пример из предыдущей статьи:

```html
<label for="name">Name:</label> <input type="text" id="name" name="user\_name" />
```

При правильно связанном элементе `<label>` с элементом `<input>` через атрибуты `for` и `id` соответственно (атрибут `for` ссылается на атрибут `id` соответствующего виджета формы), скринридер прочтёт вслух что-то наподобие "Name, edit text".

Если `<label>` не правильно установлен, скринридер прочитает это как "Edit text blank", что не несёт в себе никакой уточняющей информации, позволяющей понять предназначение данного текстового поля.

Обратите внимание на то, что виджет формы может быть вложен в элемент `<label>`, как на примере:

```html
<label for="name">
  Name: <input type="text" id="name" name="user\_name" />
</label>
```

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

### Лейблы тоже кликабельны!

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

Например:

```html
<form>
  <p>
    <label for="taste\_1">I like cherry</label>
    <input type="checkbox" id="taste\_1" name="taste\_cherry" value="1" />
  </p>
  <p>
    <label for="taste\_2">I like banana</label>
    <input type="checkbox" id="taste\_2" name="taste\_banana" value="2" />
  </p>
</form>
```

**Примечание:** вы можете посмотреть этот пример тут checkbox-label.html (также можно посмотреть код вживую).

### Несколько лейблов

На самом деле вы можете добавить несколько текстов-подсказок на один виджет формы, но это не очень хорошая идея, так как у некоторых вспомогательных технологий могут возникнуть проблемы с обработкой такой структуры. Вместо использования нескольких лейблов, лучше вложить виджет формы внутрь одного элемента `<label>`.

Рассмотрим этот пример:

```html
<p>Required fields are followed by <abbr title="required">*</abbr>.</p>

<!-- Тогда это: -->
<div>
  <label for="username">Name:</label>
  <input type="text" name="username" />
  <label for="username"><abbr title="required">*</abbr></label>
</div>

<!-- лучше сделать таким образом: -->
<div>
  <label for="username">
    <span>Name:</span>
    <input id="username" type="text" name="username" />
    <abbr title="required">*</abbr>
  </label>
</div>

<!-- Но этот вариант лучше всего: -->
<div>
  <label for="username">Name: <abbr title="required">*</abbr></label>
  <input id="username" type="text" name="username" />
</div>
```

Параграф на первой строке примера описывает правило для обязательных элементов. Вначале необходимо убедиться, что вспомогательные технологии, такие как программы чтения с экрана, отображают или озвучивают их пользователю, прежде чем он найдёт требуемый элемент. Таким образом они будут знать, что означает звёздочка. Программа чтения с экрана будет произносить звёздочку как «звёздочку» или «обязательно», в зависимости от настроек программы чтения с экрана — в любом случае, первый абзац даёт понимание того, что будет означать звёздочка далее в форме.

* В первом примере лейбл не будет прочитан вместе с текстовым полем — получится лишь "edit text blank" и отдельно читаемые тексты-подсказки. Множественные элементы `<label>` могут быть неправильно интерпретированы программой чтения с экрана.
* Второй пример немного лучше — лейбл будет прочитан вместе с текстовым полем и будет звучать как "name star name edit text", однако тексты-подсказки всё ещё разделены. Это всё ещё немного сбивает с толку, но на этот раз ситуация немного лучше, потому что с текстовое поле связано с текстом-подсказкой.
* Третий пример — лучший, так как весь лейбл будет связан с текстовым полем и озвучен целиком, а при чтении текст будет звучать как "name star edit text".

**Примечание:** В зависимости от программы для чтения с экрана результаты могут немного отличаться. В данной статье для тестирования использовался VoiceOver (NVDA ведёт себя аналогично). Также мы были бы рады, если бы вы поделились своим опытом.

**Примечание:** вы можете найти этот пример на GitHub required-labels.html (также можно посмотреть вживую). Запускайте пример, закомментировав остальные, иначе скриридеры не смогут правильно распознать контент, если у вас будет несколько лейблов и несколько текстовых полей с одинаковым ID!

Часто используемые с формами HTML-структуры
-------------------------------------------

Помимо структур, характерных только для HTML-форм, хорошо помнить, что формы — это просто HTML. Это означает, что вы можете использовать всю мощь HTML для структурирования HTML-формы.

Как вы можете заметить в примерах, оборачивать лейбл и виджет формы в элемент `<div>` — это общепринятая практика. Элемент `<p>` также часто используется, как и HTML-списки (последние часто используются для структурирования множественных чекбоксом или радио-кнопок).

В добавок к элементу `<fieldset>` часто используют HTML-заголовки (например, `<h1>` (en-US), `<h2>` (en-US)) и секционирование (например, `<section>`) для структурирования сложных форм.

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

Каждый отдельный раздел функциональности содержится в элементах `<section>` и `<fieldset>`, содержащий переключатели. Каждый отдельный раздел функциональности должен находиться в отдельном элементе `<section>` с элементами `<fieldset>`, содержащими переключатели.

### Активное обучение: построение структуры формы

Давайте применим эти идеи на практике и построим более сложноструктурируемую форму — формы оплаты. Форма будет содержать некоторые типы виджетов формы, которые вы можете пока не понять — не переживайте об этом, вы найдёте информацию в следующей статье (Основные нативные элементы управления формами (en-US)). А пока внимательно прочитайте описание, следуя приведённым ниже инструкциям, и начинайте формировать представление о том, какие элементы обёртки мы используем для структурирования формы и почему.

1. Для начала сделайте локальную копию пустого шаблона и CSS для нашей платёжной формы в новой директории на вашем компьютере.
2. Сначала подключите CSS к HTML, добавив следующую строку кода внутрь HTML-элемента `<head>`:

```html
<link href="payment-form.css" rel="stylesheet" />
```

3. Далее начните создавать свою форму с добавления внешнего элемента `<form>`:

```html
<form></form>
```

4. Внутри тега `<form>`, добавьте заголовок и параграф, информирующий пользователей о том, как помечены поля, обязательные для заполнения:

```html
<h1>Payment form</h1>
<p>
  Required fields are followed by
  <strong><abbr title="required">*</abbr></strong
 >.
</p>
```

5. Далее нам надо добавить более крупный кусок кода под нашей предыдущей записью. Здесь вы можете увидеть, что мы оборачиваем поля с контактной информацией в отдельный элемент `<section>`. Более того, у нас есть набор из двух радио-кнопок, каждую из которых мы помещаем в отдельный элемент списка (`<li>`). Наконец, у нас есть два текстовых поля `<input>` и связанные с ними элементы `<label>`, которые находятся внутри элементов `<p>`, а также поле для ввода пароля. Добавьте этот код в вашу форму:

```html
<section>
  <h2>Contact information</h2>
  <fieldset>
    <legend>Title</legend>
    <ul>
      <li>
        <label for="title\_1">
          <input type="radio" id="title\_1" name="title" value="M." />
          Mister
        </label>
      </li>
      <li>
        <label for="title\_2">
          <input type="radio" id="title\_2" name="title" value="Ms." />
          Miss
        </label>
      </li>
    </ul>
  </fieldset>
  <p>
    <label for="name">
      <span>Name: </span>
      <strong><abbr title="required">*</abbr></strong>
    </label>
    <input type="text" id="name" name="username" />
  </p>
  <p>
    <label for="mail">
      <span>E-mail: </span>
      <strong><abbr title="required">*</abbr></strong>
    </label>
    <input type="email" id="mail" name="usermail" />
  </p>
  <p>
    <label for="pwd">
      <span>Password: </span>
      <strong><abbr title="required">*</abbr></strong>
    </label>
    <input type="password" id="pwd" name="password" />
  </p>
</section>
```

6. Сейчас мы перейдём к второму разделу `<section>` нашей формы — платёжной информации. В этом разделе у нас есть три отдельных виджета формы со связанными с ними лейблами, находящимися внутри тега `<p>`. Первый — это выпадающее меню (`<select>`) для выбора типа кредитной карты. Второй — это элемент `<input>` с типом `number` для ввода номера карты. Последний виджет — это элемент `<input>` с типом date для указания даты окончания действия карты (должен будет появиться виджет с выбором даты или обычное текстовое поле в браузерах, не поддерживающих данные тип). Более новые типы полей описаны в статье The HTML5 input types (en-US).
 Вставьте следующий код под предыдущим разделом:

```html
<section>
  <h2>Payment information</h2>
  <p>
    <label for="card">
      <span>Card type:</span>
    </label>
    <select id="card" name="usercard">
      <option value="visa">Visa</option>
      <option value="mc">Mastercard</option>
      <option value="amex">American Express</option>
    </select>
  </p>
  <p>
    <label for="number">
      <span>Card number:</span>
      <strong><abbr title="required">*</abbr></strong>
    </label>
    <input type="number" id="number" name="cardnumber" />
  </p>
  <p>
    <label for="date">
      <span>Expiration date:</span>
      <strong><abbr title="required">*</abbr></strong>
      <em>formatted as mm/yy</em>
    </label>
    <input type="date" id="date" name="expiration" />
  </p>
</section>
```

7. Последняя секция, которую мы добавим выглядит намного проще и содержит в себе только элемент `<button>` с типом `submit`, для отправки данных. Добавьте этот код в конец вашей формы:

```html
<p><button type="submit">Validate the payment</button></p>
```

Вы можете увидеть законченную форму в действии ниже (также её можно найти на GitHub — посмотрите payment-form.html и живой пример):

Протестируйте себя!
-------------------

Вы дошли до конца статьи, но можете ли вспомнить самую важную информацию? Далее вы можете найти тест, который поможет убедиться, что вы усвоили знания прежде чем двигаться дальше — посмотрите Test your skills: Form structure (en-US).

Заключение
----------

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

Смотрите также
--------------

* A List Apart: *Sensible Forms: A Form Usability Checklist*
* Назад
* Обзор: Forms
* Далее
В этом разделе
--------------

* Ваша первая HTML форма
* Как структурировать HTML-формы
* Стандартные виджеты форм
* The HTML5 input types (en-US)
* Other form controls (en-US)
* Стили HTML-форм
* Advanced form styling (en-US)
* UI pseudo-classes (en-US)
* Проверка данных формы
* Отправка данных формы
### Дополнительные темы

* Как создавать пользовательские виджеты форм
* Sending forms through JavaScript
* Property compatibility table for form widgets (en-US)


Отправка данных формы - Изучение веб-разработки

Отправка данных формы
=====================

* Назад  (en-US)
* Обзор: Forms
* Далее  (en-US)

В этой статье рассматривается, что происходит, когда пользователь отправляет форму - куда передаются данные и как мы их обрабатываем, когда они туда попадают? Мы также рассмотрим некоторые проблемы безопасности, связанные с отправкой данных формы.

| Предварительные знания: | Базовая компьютерная грамотность, понимание HTML и базовые знания по HTTP и программированию на стороне сервера. |
| --- | --- |
| Задача: | Понять, что происходит при отправке данных формы, в том числе получить представление о том, как данные обрабатываются на стороне сервера. |

Куда отправляются данные?
-------------------------

Здесь мы обсудим, что происходит с данными при отправке формы.

### О клиентской/серверной архитектуре

WEB основан на очень простой клиент-серверной архитектуре, которую можно обобщить следующим образом: клиент (обычно веб-браузер) отправляет запрос на сервер (в основном веб-сервер, такой как Apache, Nginx, IIS, Tomcat, и т. д.), используя протокол HTTP. Сервер отвечает на запрос, используя тот же протокол.
 ![A basic schema of the Web client/server architecture](/ru/docs/Learn/Forms/Sending_and_retrieving_form_data/client-server.png)

На стороне клиента HTML-форма - это не более чем удобный способ настройки HTTP-запроса для отправки данных на сервер. Это позволяет пользователю предоставлять информацию для доставки в HTTP-запросе .

**Примечание:** Для получения более полного представления о том, как работают клиент-серверные архитектуры, ознакомьтесь с модулем «Первые шаги в программировании на стороне сервера».

### На стороне клиента: определение способа отправки данных

Элемент `<form>` определяет способ отправки данных. Все его атрибуты предназначены для того, чтобы вы могли настроить запрос на отправку, когда пользователь нажимает кнопку отправки. Двумя наиболее важными атрибутами являются `action` и `method`.

#### Атрибут `action`

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

В этом примере данные отправляются на абсолютный URL — `http://foo.com`:

```html
<form action="http://foo.com"></form>
```

Здесь мы используем относительный URL - данные отправляются на другой URL на сервере:

```html
<form action="/somewhere\_else"></form>
```

Если атрибуты не указаны, как показано ниже, данные из формы `<form>` отправляются на ту же страницу, на которой размещается данная форма:

```html
<form></form>
```

Многие старые страницы используют следующий синтаксис, чтобы указать, что данные должны быть отправлены на ту же страницу, которая содержит форму; это было необходимо, потому что до появления HTML5 атрибут `action` был обязательным. Это больше не нужно.

```html
<form action="#"></form>
```

**Примечание:** Можно указать URL, который использует протокол HTTPS (безопасный HTTP). Когда вы делаете это, данные шифруются вместе с остальной частью запроса, даже если сама форма размещается на небезопасной странице, доступ к которой осуществляется через HTTP. С другой стороны, если форма размещается на защищённой странице, но вы указываете небезопасный URL-адрес HTTP с атрибутом `action`, все браузеры выдают пользователю предупреждение о безопасности при каждой попытке отправки данных, поскольку данные не шифруются.

#### Атрибут `method`

Этот атрибут определяет способ отправки данных. Протокол HTTP предоставляет несколько способов выполнить запрос; Данные HTML-формы могут передаваться несколькими различными способами, наиболее распространёнными из которых являются метод `GET` и метод `POST`.

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

### Метод GET

Метод `GET` - это метод, используемый браузером, который говорит серверу, что нужно отправить назад данный ресурс: «Эй, сервер, я хочу получить этот ресурс». В этом случае браузер отправляет пустое тело. Поскольку тело пустое, если форма отправляется с использованием данного метода, данные, отправляемые на сервер, добавляются к URL-адресу.

Рассмотрим следующую форму:

```html
<form action="http://foo.com" method="get">
  <div>
    <label for="say">What greeting do you want to say?</label>
    <input name="say" id="say" value="Hi" />
  </div>
  <div>
    <label for="to">Who do you want to say it to?</label>
    <input name="to" id="to" value="Mom" />
  </div>
  <div>
    <button>Send my greetings</button>
  </div>
</form>
```

Поскольку используется метод `GET`, вы увидите URL `www.foo.com/?say=Hi&to=Mom`, который появится в адресной строке браузера при отправке формы.

![](/ru/docs/Learn/Forms/Sending_and_retrieving_form_data/url-parameters.png)Данные добавляются в URL как последовательность пар имя / значение. После того, как URL веб-адрес закончился, мы добавляем знак вопроса (`?`), за которым следуют пары имя / значение, каждая из которых разделена амперсандом (`&`). В этом случае мы передаём две части данных на сервер:

* `say`, со значением `Hi`
* `to`, со значением `Mom`

HTTP-запрос имеет следующий вид:
```

GET /?say=Hi&to=Mom HTTP/2.0
Host: foo.com
```

**Примечание:** вы можете найти этот пример на GitHub — смотрите get-method.html (see it live also).

### Метод POST

Метод `POST` немного отличается. Браузер использует этот метод для связи с сервером при запросе ответа с учётом данных, представленные в теле HTTP-запроса: «Эй, сервер, взгляни на эти данные и отправь мне соответствующий результат». Если форма отправляется с использованием этого метода, данные добавляются в тело HTTP-запроса.

Давайте рассмотрим пример — это та же самая форма, которую мы рассматривали в разделе `GET` выше, но с атрибутом `method`, установленным в `post`.

```html
<form action="http://foo.com" method="post">
  <div>
    <label for="say">What greeting do you want to say?</label>
    <input name="say" id="say" value="Hi" />
  </div>
  <div>
    <label for="to">Who do you want to say it to?</label>
    <input name="to" id="to" value="Mom" />
  </div>
  <div>
    <button>Send my greetings</button>
  </div>
</form>
```

Когда форма отправляется с использованием метода `POST`, данные добавляются не к URL-адресу, а включаются в тело запроса. HTTP-запрос имеет следующий вид:
```

POST / HTTP/2.0
Host: foo.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

say=Hi&to=Mom
```

Заголовок `Content-Length` указывает размер тела, а заголовок `Content-Type` указывает тип данных, отправляемых на сервер. Мы обсудим эти заголовки позже.

**Примечание:** вы можете найти этот пример на GitHub — смотрите post-method.html (see it live also).

#### Просмотр HTTP-запросов

HTTP-запросы никогда не отображаются пользователю (если вы хотите их видеть, вам нужно использовать такие инструменты, как Firefox Network Monitor или Chrome Developer Tools). Например, данные формы можно увидеть на вкладке Сеть (Network) в Chrome следующим образом (после отправки формы):

1. Нажмите F12
2. Выберите Network
3. Выберите "All"
4. Выберите "foo.com" во вкладке "Name"
5. Выберите "Headers"

Затем вы можете получить данные формы, как показано на рисунке ниже.

![](/ru/docs/Learn/Forms/Sending_and_retrieving_form_data/network-monitor.png)

Единственное, что отображается пользователю — вызываемый URL. Как упоминалось раннее, запрос с методом `GET` позволит пользователю увидеть информацию из запроса в URL, а запрос с методом `POST` не позволит. Две причины, почему это может быть важно:

1. Если необходимо отправить пароль (или любую другую важную информацию), никогда не используйте метод `GET`, иначе рискуете отобразить эту информацию в URL-строке, что небезопасно.
2. Если необходимо отправить большой объем информации, `POST`-метод является предпочтительным, так как некоторые браузеры ограничивают длину URL. К тому же, многие сервера так же ограничивают длину обрабатываемых URL.
### На стороне сервера: получение данных

Какой бы HTTP вы не выбрали, сервер возвращает строку, которая будет последовательно проанализирована для получения данных в формате листа с парами ключ/значение. Способ получения доступа к этому листу зависит от платформы разработки или особенностей фреймворка, который вы можете использовать. Технологии, которые вы используете, определяют, как обрабатываются скопированные ключи. Часто, приоритетным является последнее полученное значение для данного ключа.

#### Пример: Чистый PHP

PHP предлагает несколько глобальных объектов для доступа к данным. Например, вы используете `POST`-метод, в приведённом ниже примере данные просто получаются и показываются пользователю. Разумеется, как использовать данные — решать только вам. Вы можете отобразить эти данные, поместить в базу данных, отправить по почте или передать эти данные куда-либо ещё.

```php
<?php
 // The global $\_POST variable allows you to access the data sent with the POST method by name
 // To access the data sent with the GET method, you can use $\_GET
 $say = htmlspecialchars($\_POST['say']);
 $to = htmlspecialchars($\_POST['to']);

 echo $say, ' ', $to;
?>
```

Пример показывает страницу с данными, которые мы отправили. Вы можете посмотреть это в действии в нашем файле с примером: php-example.html — который содержит те же данные, которые вы видели раньше: `method` : `post` и `action` из `php-example.php`. Когда данные переданы на отправку (submit), они переданы в форму php-example.php, которая содержит PHP код из примера выше. Когда код будет выполнен, браузер выведет (output) обработанное сообщение: `Hi Mom`.

![](/ru/docs/Learn/Forms/Sending_and_retrieving_form_data/php-result.png)

**Примечание:** Этот пример не будет работать, когда вы загружаете его в браузер локально — браузер не может интерпретировать PHP код, после отправки данных из формы, браузер просто предложит загрузить PHP файл. Чтобы пример заработал, необходимо отправить его на PHP сервер. Для тестирования PHP на локальных серверах можете пробовать MAMP (Mac and Windows) и/или AMPPS (Mac, Windows, Linux).

#### Пример: Python

Этот пример показывает, как вы можете использовать Python для решения той же задачи — отобразить отправленные данные на странице. В этом примере используется Flask framework для визуализации шаблонов, поддерживающих форму отправки данных (смотри python-example.py).

```python
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def form():
    return render_template('form.html')

@app.route('/hello', methods=['GET', 'POST'])
def hello():
    return render_template('greeting.html', say=request.form['say'], to=request.form['to'])

if __name__ == "\_\_main\_\_":
    app.run()
```

Два шаблона из коде выше взаимодействуют так:

* form.html: Та же форма, что и выше The POST method , только с использованием `action` к `{{ url_for('hello') }}`. (Это Jinja2 шаблон, который изначально HTML, но может содержать вызовы Python кода в фигурных скобках, которые запустятся веб-сервером. `url_for('hello')` буквально говорит: после отправки данных переадресуй их в `/hello`.)
* greeting.html: Этот шаблон просто содержит строку, которая отображает два бита данных, переданных ему при отображении. Это сделано с помощью функции `hello()`, указанной выше, которая выполняется, когда запрос направляется в `/hello` URL.

**Примечание:** Опять же, этот код не будет работать, если вы просто попробуете загрузить его прямо в браузер. Python работает немного иначе, чем PHP — чтобы запустить этот код, нужно установить Python/PIP, потом установить Flask используя команду: `pip3 install flask`. После этого, вы сможете запустить файл из примера, используя команду: `python3 python-example.py`, потом открыть `localhost:5000` в своём браузере.

#### Другие языки и фреймворки

Существует множество других серверных технологий, которые вы можете использовать для работы с формами, включая языки Perl, Java, .Net, Ruby, и прочее. Выбирайте тот, который нравится больше. К тому же, использовать вышеупомянутые технологии непосредственно, без использования фреймворков, может быть сложно. Лучше использовать один из множества высококачественных фреймворков, таких как:

* Django для Python (немного тяжеловеснее, чем Flask, но больше инструментов и опций)
* Express для Node.js
* Laravel для PHP
* Ruby On Rails для Ruby
* Phoenix для Elixir

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

**Примечание:** Обучению фреймворкам и работе с серверами не входит в рамки этой статьи. Если хотите узнать больше, ссылки ниже помогут в этом.

Особый случай: отправка файлов
------------------------------

Отправка файлов с помощью форм HTML — это особый случай. Файлы — это бинарные данные или рассматриваются как таковые, в то время как все остальные — это текстовые данные. Поскольку HTTP — это текстовый протокол, есть особые требования для работы с бинарными данными.

### Атрибут `enctype`

Этот атрибут позволяет конкретизировать значение в `Content-Type` HTTP заголовок, включённый в запрос, при генерировании отправки формы. Этот заголовок очень важен, потому что указывает серверу, какой тип данных отправляется. По умолчанию это: `application/x-www-form-urlencoded`. На человеческом это значит: "Это форма с данными, которые были закодированы в URL параметры."

Если хотите отправить файл, нужно сделать следующие три шага:

* Указать `method` атрибут `POST`, поскольку содержимое файла, как и сам файл, не могут быть отображены в URL параметрах.
* Установить в `enctype` значение `multipart/form-data`, потому что данные будут разбиты на несколько частей: одна часть на файл (две части на два файла), и одна часть на текстовые данные (при условии, если форма содержит поле для получения тестовых данных).
* Подключите один или более File picker (en-US) виджетов, чтобы позволить своим пользователям выбрать, какие и сколько файлов будут загружены.

Пример:

```html
<form method="post" enctype="multipart/form-data">
  <div>
    <label for="file">Choose a file</label>
    <input type="file" id="file" name="myFile" />
  </div>
  <div>
    <button>Send the file</button>
  </div>
</form>
```

**Примечание:** Некоторые браузеры поддерживают `multiple` атрибут элемента `<input>` , который позволяет выбрать больше одного файла для загрузки, при использовании одного элемента `<input>` . То, как сервер работает с этими файлами, напрямую зависит от технологий, используемых на сервере. Как упоминалось ранее, использование фреймворков сделает вашу жизнь намного легче.

**Предупреждение:** Многие сервера имеют заданные ограничения на размер загружаемых файлов и запросы от пользователей, чтобы защититься от возможных злоупотреблений. Важно проверять эти ограничения у администратора сервера, прежде чем загружать файлы.

Проблемы безопасности
---------------------

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

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

### XSS "Межсайтовый скриптинг" и CSRF "Подделка межсайтовых запросов"

Межсайтовый скриптинг (XSS "Cross Site Request Forgery") и подделка межсайтовых запросов (CSRF "Cross-Site Scripting") - это распространённые типы атак, которые происходят при отображении данных после ответа сервера или другого пользователя.

Межсайтовый скриптинг (XSS "Cross Site Request Forgery") позволяет злоумышленникам внедрить клиентский скрипт в веб-страницы, просматриваемые другими пользователями. Подделка межсайтовых запросов (CSRF "Cross-Site Scripting") может использоваться злоумышленниками для обхода средств контроля доступа, таких как одна и та же политика происхождения. Последствие от этих атак может варьироваться от мелких неудобств до значительного риска безопасности.

CSRF-атаки аналогичны XSS-атакам в том, что они начинаются одинаково - с внедрения клиентского скрипта в веб-страницы - но их конечные цели разные. Злоумышленники CSRF пытаются назначить права пользователям с более высоким уровнем прав доступа(например, администратору сайта), чтобы выполнить действие, которое они не должны выполнять (например, отправка данных ненадёжному пользователю). Атаки XSS используют доверие пользователя к веб-сайту, в то время как атаки CSRF используют доверие веб-сайта к пользователю.

Чтобы предотвратить эти атаки, вы всегда должны проверять данные, которые пользователь отправляет на ваш сервер, и (если вам нужно отобразить их) стараться не отображать HTML-контент, предоставленный пользователем. Вместо этого вы должны обработать предоставленные пользователем данные, чтобы не отображать их слово в слово. Сегодня почти все платформы на рынке реализуют минимальный "фильтр", который удаляет элементы HTML `<script>`, `<iframe>` (en-US) и `<object>` (en-US) полученных от любого пользователя. Это помогает снизить риск, но не исключает его полностью.

### SQL - вброс

SQL -вброс представляет собой тип атак, при которых осуществляется попытка выполнения действия с базой данных, используемой целевым веб-сайтом. В этих случаях обычно осуществляется отправка SQL-запроса в надежде, что сервер выполнит этот запрос (обычно при попытке сервера приложения сохранить данные, отправляемые пользователем). Данный вид атак является одним из самых направленных атак на веб-сайты.

Последствия могут быть ужасающими, начиная от потери данных и заканчивая утратой контроля над всей инфраструктурой веб-сайта за счёт повышения привилегий. Это очень серьёзная угроза, поэтому никогда не сохраняйте данные, отправляемые пользователем, без выполнения фильтрации данных (например, с помощью `mysqli_real_escape_string()`.

### Вброс HTTP-заголовка и email

Эти виды атак могут проявляться, когда ваше приложение создаёт заголовки HTTP или электронные почтовые адреса на основании данных, введённых пользователем в форму. Такие атаки напрямую не повреждают сервер или пользователей, однако создают уязвимость для таких угроз, как перехват сессии, или для фишинговых атак.

Такие атаки являются самыми незаметными, но при этом могут превратить ваш сервер в зомби.

### Будьте параноиком: никогда не доверяйте вашим пользователям

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

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

* Избегайте потенциально опасных символов. Конкретные символы, с которыми следует соблюдать осторожность, зависят от контекста, в котором используются данные, а также от используемой платформы. Однако, все языки на стороне сервера имеют соответствующие функции для обеспечения такой защиты.
* Ограничьте входящий объем данных для поступления только реально необходимых данных.
* Помещайте загруженные файлы в песочницу (храните их на другом сервере и предоставляйте доступ к файлам только через отдельный поддомен или даже через совершенно другое доменное имя).

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

**Примечание:** В статье Безопасность веб-сайта нашего раздела серверного обучения приведено подробное обсуждение упомянутых угроз и возможных способов их устранения.

Заключение
----------

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

Смотрите также
--------------

Если вы хотите узнать больше об обеспечении безопасности веб-приложений, вы можете использовать следующие источники информации:

* Первые шаги в программирование веб-сайтов на стороне сервера
* Открытый проект обеспечения веб-безопасности приложений (OWASP)
* Веб-безопасность от Mozilla
* Назад  (en-US)
* Обзор: Forms
* Далее  (en-US)


Валидация форм на стороне клиента - Изучение веб-разработки

Валидация форм на стороне клиента
=================================

* Назад  (en-US)
* Обзор: Forms
* Далее

Перед отправкой данных на сервер важно убедиться, что все обязательные поля формы заполнены данными в корректном формате. Это называется **валидацией на стороне клиента** и помогает убедиться, что данные, введённые в каждый элемент формы, соответствуют требованиям. Данная статья проведёт вас через основные концепци и примеры валидации на стороне клиента.

| Начальные требования: | Владение компьютером, достаточное понимание HTML, CSS, и JavaScript. |
| --- | --- |
| Цель: | Понять, что такое валидация на стороне клиента, почему это важно и как применять различные техники для её реализации. |

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

Однако, *не следует рассматривать* валидацию на стороне клиента как достаточную меру безопасности! Любые данные, отправляемые через форму, необходимо *дополнительно* проверять на безопасность и *на стороне сервера*, поскольку валидацию на стороне клиента достаточно просто обойти и она может не остановить злоумышленников. Чтобы лучше понимать потенциальные угрозы, рекомендуем ознакомиться с разделом Безопасность вебсайтов; валидация на стороне сервера выходит за рамки этого модуля, но о ней следует помнить.

Что такое валидация формы?
--------------------------

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

* "Обязательное поле" (Вы не можете оставить поле пустым).
* "Пожалуйста, введите номер телефона в формате xxx-xxxx" (Чтобы данные считались корректными, их необходимо указать в определённом формате).
* "Пожалуйста, введите корректный email-адрес" (вы ввели данные в неправильном формате).
* "Длина пароля должна быть от 8 до 30 символов и включать одну заглавную букву, один символ, и одну цифру." (Требования к формату данных достаточно конкретные).

Это называется **валидацией формы**. По мере ввода, браузер и/или сервер проверяют данные, чтобы определить, соответствуют ли они требуемому формату. Валидация, выполняемая в браузере, называется **валидацией на стороне клиента**, а выполняемая на сервере — **валидацией на стороне сервера**. В этом разделе мы сосредоточимся на валидации, выполняемой на стороне клиента.

Если формат корректен, приложение позволяет отправить данные на сервер и (обычно) сохранить в базу данных; в противном случае выводится сообщение с описанием того, что нужно исправить, позволяя ввести данные снова.

Мы хотим максимально упростить заполнение веб-форм. Тогда почему мы настаиваем валидации данных? На это есть три основные причины:

* **Мы хотим получать правильные данные в правильном формате.** Наши приложения не будут работать должным образом, если данные от пользователей хранятся в неправильном формате, некорректны сами по себе или вовсе пропущены.
* **Мы хотим защитить данные пользователей**. Принуждение пользователей вводить надёжные пароли облегчает защиту их аккаунтов.
* **Мы хотим защитить себя**. Существует множество способов, позволяющих злоумышленникам с помощью незащищённых форм навредить приложению (смотрите Безопасность вебсайтов).

**Предупреждение:** Никогда не доверяйте данным, передаваемым на сервер клиентской программой. Даже если ваша форма правильно валидируется и не допустит введение потенциально вредоносных данных на стороне клиента, злоумышленники по-прежнему могут изменить сетевой запрос.
Типы валидации на стороне клиента
---------------------------------

Существует два типа валидации на стороне клиента, с которыми вы столкнётесь в Интернете:

* **Встроенная валидация форм** использует функционал валидации HTML5, который мы неоднократно обсуждали в этом модуле. HTML5-валидация обычно не требует большого количества JavaScript-кода и демонстрирует лучшую производительность, но не настолько настраиваема, как валидация с помощью JavaScript.
* **JavaScript-валидация** кодируется с помощью JavaScript. Она полностью настраиваема, но требует программирования всей логики (или использования библиотеки).
Использование встроенной валидации форм
---------------------------------------

Одной из самых важных функций элементов форм HTML5 (en-US) является способность валидировать бóльшую часть пользовательских данных без использования JavaScript. Это выполняется с помощью атрибутов валидации у элементов формы. Многие из них мы уже рассмотрели в этом курсе:

* `required` (en-US): Определяет, что для отправки формы данное поле предварительно должно быть заполнено.
* `minlength` (en-US) и `maxlength` (en-US): Задаёт минимальную и максимальную длину текстовых данных (строк)
* `min` (en-US) и `max` (en-US): Задаёт минимальное и максимальное значение для поля, расчитанного на числовой тип данных
* `type`: Определяет тип данных, на который рассчитано поле: число, email-адрес или какой-то другой предустановленный тип
* `pattern`: С помощью регулярного выражения, определяет шаблон, которому должны соответствовать вводимые данные.

Если данные, введённые в поле формы, соответствуют правилам перечисленных выше атрибутов, они считаются валидными, если нет — не валидными

Когда элемент валиден, справедливы следующие утверждения:

* Элемент соответствует CSS-псевдоклассу `:valid`, позволяющему стилизовать только валидные элементы.
* Если пользователь пытается отправить данные, браузер отправит форму при условии, что ничто другое (например, JavaScript) не помешает ему это сделать

Когда элемент не валиден, справедливы следующие утверждения:

* Элемент соответствует CSS-псевдоклассу `:invalid` или, в зависимости от ошибки, другим псевдоклассам (например, `:out-of-range`), которые позволяют применять определённые стили к элементам, не являющимся валидными.
* Если пользователь пытается отправить данные, браузер заблокирует форму и выведет сообщение об ошибке.

**Примечание:** Существует ошибки, которые не позволяют отправлять форму, в частности `badInput`, `patternMismatch`, `rangeOverflow` или `rangeUnderflow`, `stepMismatch`, `tooLong` или `tooShort`, `typeMismatch`, `valueMissing` (en-US), или `customError`.

Примеры встроенной валидации форм
---------------------------------

В этом разделе мы протестируем некоторые из атрибутов, которые обсуждали выше.

### Простой начальный файл

Давайте начнём с простого примера: поле, позволяющее указать своё предпочтение — банан или вишня. Этот пример включает обычное текстовое поле `<input>`, связанный с ним элемент `<label>` и кнопку отправки формы `<button>`. Исходный код можно найти на GitHub по адресу fruit-start.html, а ниже приведён рабочий пример.

```html
<form>
  <label for="choose">Would you prefer a banana or cherry?</label>
  <input id="choose" name="i\_like" />
  <button>Submit</button>
</form>
```

```css
input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}
```

Для начала скопируйте файл `fruit-start.html` в новую папку на вашем жёстком диске.

### Атрибут required

Самым простым в HTML5-валидации является атрибут `required` (en-US). Добавьте его к элементу, чтобы сделать заполнение обязательным. Элемент с данным атрибутом соответствует CSS-псевдоклассу `:required`, а если поле ввода пустое, вместо отправки формы отобразится сообщение об ошибке. Пока поле пустое, оно также будет соответствовать CSS-псевдоклассу `:invalid`.

Добавьте к полю атрибут `required`, как показано ниже.

```html
<form>
  <label for="choose">Would you prefer a banana or cherry? (required)</label>
  <input id="choose" name="i\_like" required />
  <button>Submit</button>
</form>
```

Обратите внимание на CSS, который включён в файл примера:

```css
input:invalid {
  border: 2px dashed red;
}

input:invalid:required {
  background-image: linear-gradient(to right, pink, lightgreen);
}

input:valid {
  border: 2px solid black;
}
```

Данный CSS задаёт полю красную пунктирную рамку, когда оно не валидно, а когда валидно — сплошную чёрную. Мы также добавили фоновый градиент для обязательных не валидных полей. Проверьте новое поведение в примере ниже:

**Примечание:** Рабочий пример можно найти на GitHub по адресу fruit-validation.html (отдельно можно найти исходный код.)

Попробуйте отправить форму без введения значения. Обратите внимание, что не валидное поле получает фокус, появляется сообщение об ошибке ("Заполните это поле") и блокируется отправка формы.

Наличие атрибута `required` у любого элемента, который его поддерживает, означает, что элемент соответствует CSS-псевдоклассу `:required`, независимо от того, имеет он значение или нет. Если элемент `<input>` не содержит значение, он будет соответствовать псевдоклассу `:invalid`.

**Примечание:** Для повышения удобства взаимодействия указывайте пользователям, какие поля являются обязательными. К тому же, этого требует руководство по обеспечению доступности WCAG. Требуйте обязательного ввода только тех данных, которые вам действительно нужны: например, так ли важно знать пол или должность пользователя?

### Валидация с помощью регулярного выражения

Ещё одной полезной функцией валидации является атрибут `pattern`, который в виде значения принимает Регулярное выражение. Регулярное выражение (regex) — это шаблон, который может быть использован для сопоставления набора символов в текстовой строке, поэтому они идеально подходят для валидации формы и используются для множества других целей в JavaScript.

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

* `a` — Соответствует одному символу `a` (не `b`, не `aa`, и так далее).
* `abc` — Соответствует символу `a`, за которой следует `b`, за которой следует `c`.
* `ab?c` — Соответствует символу `a`, за которым опционально может следовать `b`, за которым следует `c`. ( `ac` или `abc`)
* `ab*c` — Соответствует символу `a`, за которым опционально может следовать любое количество символов `b`, за которыми следует `c`. ( `ac` , `abc`, `abbbbbc`, и так далее).
* `a|b` — Соответствует символу `a` или `b`.
* `abc|xyz` — Соответствует в точности `abc` или в точности `xyz` (но не `abcxyz` или `a` или `y`, и так далее).

Есть еще много возможностей, которые мы не упомянули. Полный список со множеством примеров можно найти в документации по Регулярным выражениям

Давайте рассмотрим пример. Добавьте в атрибут `pattern` следующий шаблон:

```html
<form>
  <label for="choose">Would you prefer a banana or a cherry?</label>
  <input id="choose" name="i\_like" required pattern="[Bb]anana|[Cc]herry" />
  <button>Submit</button>
</form>
```

```
input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}
```

Это даёт нам следующее обновление — опробуйте его:

**Примечание:** Рабочий пример можно найти на GitHub по адресу fruit-pattern.html (исходный код.)

В этом примере элемент `<input>` принимает одно из четырёх возможных значений: строку "banana", "Banana", "cherry", или "Cherry". Регулярные выражения чувствительны к регистру, но с помощью шаблона "Aa", вложенного в квадратные скобки, мы сделали поддержку написания слова как с большой, так и с маленькой буквы.

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

Если не пустое значение элемента `<input>` не соответствует шаблону регулярного выражения, `input` будет соответствовать псевдоклассу `:invalid`.

**Примечание:** Некоторым типам элементов `<input>` для валидации с помощью регулярного выражения не требуется атрибут `pattern`. Например, поле с типом `email` валидирует значение по шаблону одного email-адреса или, если присутствует атрибут `multiple` (en-US), шаблону списка email-адресов, разделённых запятыми.

**Примечание:** Элемент `<textarea>` (en-US) не поддерживает атрибут `pattern`.

### Ограничение длины вводимых значений

Можно ограничить максимально допустимое количество символов для текстовых полей `<input>` или `<textarea>` (en-US) используя атрибуты `minlength` (en-US) и `maxlength` (en-US). Поле будет не валидным, если количество символов его содержимого будет меньше `minlength` (en-US) или больше `maxlength` (en-US).

Зачастую браузеры не позволяют пользователям вводить в текстовое поле значение, длина которого превышает максимально допустимую. Можно существенно повысить удобство использования, если помимо ограничения в атрибуте `maxlength` добавить доступный индикатор, отображающий текущее и максимально допустимое количество символов, что даст пользователю возможность уместить содержимое в заданные рамки. Хорошим примером является окно написания твита в Twitter. Для реализации такого функционала можно использовать JavaScript, включая решения, использующие `maxlength`.

### Ограничение допустимых значений

В полях, предназначенных для ввода чисел (например, `<input type="number">`), диапазон допустимых значений можно определить с помощью атрибутов `min` (en-US) и `max` (en-US). Если поле содержит значение за пределами данного диапазона, оно будет не валидным.

Давайте рассмотрим другой пример. Создайте новую копию файла fruit-start.html.

Содержимое элемента `<body>` замените на:

```html
<form>
  <div>
    <label for="choose">Would you prefer a banana or a cherry?</label>
    <input
 type="text"
 id="choose"
 name="i\_like"
 required
 minlength="6"
 maxlength="6" />
  </div>
  <div>
    <label for="number">How many would you like?</label>
    <input type="number" id="number" name="amount" value="1" min="1" max="10" />
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>
```

* Здесь мы в поле с типом `text` атрибутам `minlength` и `maxlength`, задали одинаковое значение 6, что соответствует количеству символов в словах banana и cherry.
* В поле с типом `number` атрибуту `min` мы задали значение 1, а атрибуту `max` значение 10. При вводе чисел за пределами данного диапазона, поле будет становиться не валидным; с помощью стрелок увеличения/уменьшения пользователи не смогут выйти за границы диапазона. Текущее поле не является обязательным для заполнения, поэтому даже после очистки будет оставаться валидным.
```

input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}

div {
  margin-bottom: 10px;
}
```

Демонстрационный пример:

**Примечание:** Рабочий пример можно найти на GitHub по адресу fruit-length.html (исходный код.)

**Примечание:** `<input type="number">` (и другие типы, такие как `range` и `date`) могут также принимать атрибут `step` (en-US), который задаёт шаг увеличения или уменьшения значения при использовании кнопок вверх и вниз. В примере выше мы явно не указывали атрибут `step`, поэтому он получает значение по умолчанию, равное `1`. Это значит, что дробные числа, такие как 3.2, будут не валидными.

### Полный пример

Ниже представлен полный пример, демонстрирующий использование встроенного функционала валидации. Сначала немного HTML:

```html
<form>
  <p>
    <fieldset>
      <legend>Do you have a driver's license?<abbr title="This field is mandatory" aria-label="required">*</abbr></legend>
      <!-- Так как в группе радио-кнопок, имеющих одинаковое имя, выбранной может быть
 только одна, то и атрибут "required" достаточно задать хотя бы одной кнопке,
 чтобы сделать всю группу обязательной для заполнения -->
      <input type="radio" required name="driver" id="r1" value="yes"><label for="r1">Yes</label>
      <input type="radio" required name="driver" id="r2" value="no"><label for="r2">No</label>
    </fieldset>
  </p>
  <p>
    <label for="n1">How old are you?</label>
    <!-- Атрибут "pattern" может выступать фолбэком для браузеров, которые
 не поддерживают поля ввода c числовым типом данных. Те браузеры,
 которые такие поля поддерживают, будут просто игнорировать его.
 Как раз, ниже атрибут "pattern" выполняет роль фолбека.
 -->
    <input type="number" min="12" max="120" step="1" id="n1" name="age"
 pattern="\d+">
  </p>
  <p>
    <label for="t1">What's your favorite fruit?<abbr title="This field is mandatory" aria-label="required">*</abbr></label>
    <input type="text" id="t1" name="fruit" list="l1" required
 pattern="[Bb]anana|[Cc]herry|[Aa]pple|[Ss]trawberry|[Ll]emon|[Oo]range">
    <datalist id="l1">
      <option>Banana</option>
      <option>Cherry</option>
      <option>Apple</option>
      <option>Strawberry</option>
      <option>Lemon</option>
      <option>Orange</option>
    </datalist>
  </p>
  <p>
    <label for="t2">What's your e-mail address?</label>
    <input type="email" id="t2" name="email">
  </p>
  <p>
    <label for="t3">Leave a short message</label>
    <textarea id="t3" name="msg" maxlength="140" rows="5"></textarea>
  </p>
  <p>
    <button>Submit</button>
  </p>
</form>
```

И немного CSS для стилизации HTML:

```css
form {
  font: 1em sans-serif;
  max-width: 320px;
}

p > label {
  display: block;
}

input[type="text"],
input[type="email"],
input[type="number"],
textarea,
fieldset {
  width: 100%;
  border: 1px solid #333;
  box-sizing: border-box;
}

input:invalid {
  box-shadow: 0 0 5px 1px red;
}

input:focus:invalid {
  box-shadow: none;
}
```

Получим следующее:

В статье Атрибуты валидации (en-US) можно найти полный список атрибутов, которые можно использовать для ограничения допустимых значений ввода и типов полей `input`, которые их поддерживают.

**Примечание:** Рабочий пример можно найти на GitHub по адресу full-example.html (исходный код.)

Валидация форм с помощью JavaScript
-----------------------------------

Если нужно управлять внешним видом встроенных сообщений об ошибке или работать с устаревшими браузерами, которые не поддерживают встроенную валидацию форм HTML, вам следует использовать JavaScript. В данном разделе мы рассмотрим различные способы делать это.

### Constraint Validation API

Большинство браузеров поддерживают Constraint Validation API (en-US), который состоит из набора свойств и методов, доступных на DOM-интерфейсах следующих элементов форм:

* `HTMLButtonElement` (представляет элемент `<button>`)
* `HTMLFieldSetElement` (en-US) (представляет элемент `<fieldset>`)
* `HTMLInputElement` (en-US) (представляет элемент `<input>`)
* `HTMLOutputElement` (en-US) (представляет элемент `<output>`)
* `HTMLSelectElement` (en-US) (представляет элемент `<select>`)
* `HTMLTextAreaElement` (en-US) (представляет элемент `<textarea>` (en-US))

Для перечисленных выше элементов Constraint Validation API делает доступными следующие свойства.

* `validationMessage`: Возвращает локализованное сообщение, описывающее ограничения валидации (если таковые имеются), которым не удовлетворяет определённый элемент. Если элемент не участвует в валидации (`willValidate` установлено в `false`) или значение элемента удовлетворяет установленным ограничениям (является валидным), будет возвращена пустая строка.
* `validity`: Возвращает объект `ValidityState`, который содержит несколько свойств, описывающих состояние валидности элемента. Подробное описание всех свойств доступности можно найти на странице справочника `ValidityState`; ниже приведён список наиболее используемых:
    + `patternMismatch`: Возвращает `true`, если значение не соответствует шаблону, указанному в атрибуте `pattern`, и `false` если соответствует. Если true, элемент соответствует CSS-псевдоклассу `:invalid`.
    + `tooLong`: Возвращает `true`, если значение длиннее максимальной длины, указанной в атрибуте `maxlength`, и `false` если оно короче или равно ей. Если true, элемент соответствует CSS-псевдоклассу `:invalid`.
    + `tooShort`: Возвращает `true`, если значение короче минимальной длины, указанной в атрибуте `minlength`, и `false` если оно длинее или равно ей. Если true, элемент соответствует CSS-псевдоклассу `:invalid`.
    + `rangeOverflow`: Возвращает `true`, если значение больше указанного в атрибуте `max` максимума, и `false` если меньше или равно ему. Если true, элемент соответствует CSS-псевдоклассам `:invalid` и `:out-of-range`
    + `rangeUnderflow`: Возвращает `true`, если значение меньше указанного в атрибуте `min`, и `false` если больше или равно ему. Если true, элемент соответствует CSS-псевдоклассу `:invalid` и `:out-of-range`.
    + `typeMismatch`: Возвращает `true`, если значение не соответствует требуемому синтаксису (когда для `type` задано значение `email` или `url`), и `false` если синтаксис корректный. Если `true`, элемент соответствует CSS-псевдоклассу `:invalid`.
    + `valid`: Возвращает `true`, если элемент соответствует всем ограничениям валидации — следовательно, считается валидным, и `false` если не соответствует какому-то ограничению. Если true, элемент соответствует CSS-псевдоклассу `:valid`; иначе `:invalid`.
    + `valueMissing`: Возвращает `true`, если у элемента есть атрибут `required`, но не введено значенение, иначе возвращает `false`. Если true, элемент соответствует CSS-псевдоклассу `:invalid`.
* `willValidate`: Возвращает `true`, если элемент будет участвовать в валидации при отправке формы; иначе возвращает `false`.

Также для перечисленных выше элементов Constraint Validation API делает доступными следующие методы.

* `checkValidity()`: Возвращает `true`, если значение элемента проходит валидацию, иначе возвращает `false`. Если элемент не валиден, данный метод также запускает на нём событие `invalid`.
* `setCustomValidity(message)`: Позволяет добавить в элемент кастомное сообщение об ошибке; при этом элемент будет считаться не валидным и отобразится указанная ошибка. Это позволяет использовать JavaScript-код, чтобы представить ошибку валидации иначе, чем это предусмотрено стандартными средствами валидации HTML5. При сообщении об ошибке данное кастомное сообщение показывается пользователю.

#### Реализация кастомного сообщения об ошибке

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

У этих автоматических сообщений есть два недостатка:

* Не существует стандартного способа их стилизации с помощью CSS.
* Они зависят от локали браузера, из-за чего страница может быть на одном языке, а сообщение об ошибке — на другом, как показано на следующем скриншоте браузера Firefox.

![Пример сообщения об ошибке на англоязычной странице в браузере Firefox с настроенным французским языком](/ru/docs/Learn/Forms/Form_validation/error-firefox-win7.png)

Настройка таких сообщений об ошибках является одной из наиболее распространённых причин использования Constraint Validation API (en-US). Давайте рассмотрим простой пример, как это делается.

Начнём с простого HTML (Не стесняйтесь поместить это в пустой HTML-файл. Вы можете взять за основу свежую копию fruit-start.html, если хотите):

```html
<form>
  <label for="mail"
 >I would like you to provide me with an e-mail address:</label
 >
  <input type="email" id="mail" name="mail" />
  <button>Submit</button>
</form>
```

Добавьте на страницу следующий JavaScript:

```js
const email = document.getElementById("mail");

email.addEventListener("input", function (event) {
  if (email.validity.typeMismatch) {
    email.setCustomValidity("I am expecting an e-mail address!");
  } else {
    email.setCustomValidity("");
  }
});
```

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

В коде обработчика мы проверяем, возвращает ли свойство поля email `validity.typeMismatch` значение `true`, что значит, что содержащееся значение не соответствует шаблону корректного email-адреса. Если возвращается `true`, мы вызываем метод `setCustomValidity()` (en-US) с кастомным сообщением. Это делает поле не валидным, поэтому попытка отправить форму приводит к ошибке и отображается кастомное сообщение об ошибке.

Если свойство `validity.typeMismatch` возвращает `false`, мы вызываем метод `setCustomValidity()` с пустой строкой. Это делает поле валидным, поэтому форма может быть успешно отправлена.

Попробовать пример можно ниже:

**Примечание:**: Данный пример можно найти на GitHub по адресу custom-error-message.html (отдельно можно найти исходный код.)

#### Более подробный пример

Теперь, когда мы разобрали простой пример, давайте посмотрим, как можно использовать данный API для создания более сложной валидацию.

Во-первых, HTML. Опять же, не стесняйтесь писать его вместе с нами:

```html
<form novalidate>
  <p>
    <label for="mail">
      <span>Please enter an email address:</span>
      <input type="email" id="mail" name="mail" required minlength="8" />
      <span class="error" aria-live="polite"></span>
    </label>
  </p>
  <button>Submit</button>
</form>
```

Эта простая форма использует атрибут `novalidate`, который отключает автоматическую валидацию браузером; это позволяет нашему скрипту взять управление валидацией на себя. Однако, это не отменяет поддержку Constraint Validation API или псевдоклассов, таких как `:valid` или ему подобных. Это значит, что хотя браузер автоматически и не проверяет валидность формы перед отправкой данных, вы можете сделать это самостоятельно и соответствующим образом стилизовать форму.

Объектом валидации является обязательный для заполнения `<input type="email">` (en-US), длина которого не должна быть меньше 8 символов. Давайте напишем код, проверяющий эти критерии, и покажем кастомное сообщение об ошибке в случае несоблюдения какого-то из них.

Мы хотим показывать сообщение об ошибке внутри элемента `<span>`. Данному элементу задан атрибут `aria-live` (en-US), чтобы гарантировать, что наше кастомное сообщение об ошибке будет доступно всем, включая пользователей скринридеров.

**Примечание:** Ключевым моментом здесь является то, что добавление к форме атрибута `novalidate` отключает отображение встроенных сообщений об ошибке и позволяет вместо этого добавлять в DOM кастомные сообщения.

Перейдём к базовому CSS, чтобы немного улучшить внешний вид формы и обеспечить визуальную обратную связь при введении не валидных данных:

```css
body {
  font: 1em sans-serif;
  width: 200px;
  padding: 0;
  margin: 0 auto;
}

p \* {
  display: block;
}

input[type="email"] {
  -webkit-appearance: none;
  appearance: none;

  width: 100%;
  border: 1px solid #333;
  margin: 0;

  font-family: inherit;
  font-size: 90%;

  box-sizing: border-box;
}

/\* Это стили для не валидных полей \*/
input:invalid {
  border-color: #900;
  background-color: #fdd;
}

input:focus:invalid {
  outline: none;
}

/\* Это стили для кастомных сообщений об ошибке \*/
.error {
  width: 100%;
  padding: 0;

  font-size: 80%;
  color: white;
  background-color: #900;
  border-radius: 0 0 5px 5px;

  box-sizing: border-box;
}

.error.active {
  padding: 0.3em;
}
```

Теперь давайте рассмотрим JavaScript, который реализует кастомную валидацию.

```js
// Существуют разные способы получить DOM-узел; здесь мы определяем саму форму и
// поле ввода email и элемент span, в который поместим сообщение об ошибке
const form = document.getElementsByTagName("form")[0];

const email = document.getElementById("mail");
const emailError = document.querySelector("#mail + span.error");

email.addEventListener("input", function (event) {
  // Каждый раз, когда пользователь что-то вводит,
  // мы проверяем, являются ли поля формы валидными

  if (email.validity.valid) {
    // Если на момент валидации какое-то сообщение об ошибке уже отображается,
    // если поле валидно, удаляем сообщение
    emailError.textContent = ""; // Сбросить содержимое сообщения
    emailError.className = "error"; // Сбросить визуальное состояние сообщения
  } else {
    // Если поле не валидно, показываем правильную ошибку
    showError();
  }
});

form.addEventListener("submit", function (event) {
  // Если поле email валдно, позволяем форме отправляться

  if (!email.validity.valid) {
    // Если поле email не валидно, отображаем соответствующее сообщение об ошибке
    showError();
    // Затем предотвращаем стандартное событие отправки формы
    event.preventDefault();
  }
});

function showError() {
  if (email.validity.valueMissing) {
    // Если поле пустое,
    // отображаем следующее сообщение об ошибке
    emailError.textContent = "You need to enter an e-mail address.";
  } else if (email.validity.typeMismatch) {
    // Если поле содержит не email-адрес,
    // отображаем следующее сообщение об ошибке
    emailError.textContent = "Entered value needs to be an e-mail address.";
  } else if (email.validity.tooShort) {
    // Если содержимое слишком короткое,
    // отображаем следующее сообщение об ошибке
    emailError.textContent = `Email should be at least ${email.minLength} characters; you entered ${email.value.length}.`;
  }

  // Задаём соответствующую стилизацию
  emailError.className = "error active";
}
```

Комментарии объясняют логику хорошо, но кратко:

* При каждом изменении значения поля, мы производим его валидацию. Если данные валидны, удаляем ранее отображаемые сообщения об ошибках. Если данные не валдны, запускаем `showError()`, чтобы показать соответствующую ошибку.
* При каждой попытке отправить форму, мы снова производим валидацию. Если данные валидны, позволяем отправку формы. Если данные не валидны, запускам `showError()`, чтобы показать соответствующее сообщение об ошибке, а также предотвращаем отправку формы с помощью `preventDefault()`.
* Функция `showError()` использует различные свойства объекта `validity` поля ввода, чтобы определить тип ошибки и отобразить соответсвущее сообщение.

Рабочий пример:

**Примечание:** Рабочий пример можно найти на GitHub по адресу detailed-custom-validation.html (отдельно можно найти исходный код.)

Constraint Validation API явяется мощным инструментом валидации форм, позволяющим получить контроль над пользовательским интерфейсом, существенно превосходящий возможности HTML и CSS.

**Примечание:** Для получения дополнительной информации смотрите руководства Constraint validation guide (en-US) и Constraint Validation API (en-US).

### Проверка форм без встроенного API

В некоторых случаях, например, при необходимости поддержки устаревших браузеров или кастомных элементов формы, вы не сможете или не захотите использовать Constraint Validation API. Вы по-прежнему сможете использовать JavaScript для валидации форм, но для этого всё нужно будет писать самостоятельно.

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

Какую тип валидации я должен выполнить?

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

Что мне нужно делать, если форма не проходит валидацию?

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

Как я могу помочь пользователю исправить не валидные данные?

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

* SmashingMagazine: Form-Field Validation: The Errors-Only Approach
* SmashingMagazine: Web Form Validation: Best Practices and Tutorials
* WebFX: 10 Tips for Optimizing Web Form Submission Usability
* A List Apart: Inline Validation in Web Forms

#### Пример без использования Constraint Validation API

Чтобы проиллюстрировать это дальше приводится упрощённая версия предыдущего примера, которая работает с устаревшими браузерами.

HTML почти тот такой же; мы только удалили функционал валидации HTML5.

```html
<form>
  <p>
    <label for="mail">
      <span>Please enter an email address:</span>
      <input type="text" id="mail" name="mail" />
      <span class="error" aria-live="polite"></span>
    </label>
  </p>
  <!-- Для некоторых устаревших браузеров элементу `button` нужно добавлять
 атрибут `type` с явно заданным значением `submit` -->
  <button type="submit">Submit</button>
</form>
```

CSS также не требует особых изменений; мы только заменили CSS-псевдокласс `:invalid` на реальный класс и не использовали селектор по атрибутам, так как он не работает в Internet Explorer 6.

```css
body {
  font: 1em sans-serif;
  width: 200px;
  padding: 0;
  margin: 0 auto;
}

form {
  max-width: 200px;
}

p \* {
  display: block;
}

input.mail {
  -webkit-appearance: none;

  width: 100%;
  border: 1px solid #333;
  margin: 0;

  font-family: inherit;
  font-size: 90%;

  box-sizing: border-box;
}

/\* Стилизация не валидных полей \*/
input.invalid {
  border-color: #900;
  background-color: #fdd;
}

input:focus.invalid {
  outline: none;
}

/\* Стилизация сообщений об ошибках \*/
.error {
  width: 100%;
  padding: 0;

  font-size: 80%;
  color: white;
  background-color: #900;
  border-radius: 0 0 5px 5px;
  box-sizing: border-box;
}

.error.active {
  padding: 0.3em;
}
```

Существенно изменился только JavaScript-код, который теперь должен выполнять гораздо больше работы.

```js
// Устаревшие браузеры поддерживают несколько способов получения DOM-узла
const form = document.getElementsByTagName("form")[0];
const email = document.getElementById("mail");

// Ниже приведён способ получения узла следующего родственного DOM-элемента
// Он опасен, потому что можно создать бесконечный цикл.
// В современных браузерах лучше использовать `element.nextElementSibling`
let error = email;
while ((error = error.nextSibling).nodeType != 1);

// Согласно спецификации HTML5
const emailRegExp =
  /^[a-zA-Z0-9.!#$%&'\*+/=?^\_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)\*$/;

// Многие устаревшие браузеры не поддерживают метод `addEventListener`
// Есть простой способ заменить его; и далеко не единственный
function addEvent(element, event, callback) {
  let previousEventCallBack = element["on" + event];
  element["on" + event] = function (e) {
    let output = callback(e);

    // Колбэк, который возвращает `false`, останавливает цепочку колбэков
    // и прерывает выполнение колбэка события
    if (output === false) return false;

    if (typeof previousEventCallBack === "function") {
      output = previousEventCallBack(e);
      if (output === false) return false;
    }
  };
}

// Теперь мы можем изменить наши критерии валидации
// Поскольку мы не полагаемся на CSS-псевдокласс, для поля email
// нужно явно задать валидный / не валидный класс
addEvent(window, "load", function () {
  // Проверка, является ли поле пустым (помните, оно не являтеся обязательным)
  // Если поле не пустое, проверяем содержимое на соответствует шаблону email
  const test = email.value.length === 0 || emailRegExp.test(email.value);

  email.className = test ? "valid" : "invalid";
});

// Здесь определяется поведение при вводе пользователем значения поля
addEvent(email, "input", function () {
  const test = email.value.length === 0 || emailRegExp.test(email.value);
  if (test) {
    email.className = "valid";
    error.textContent = "";
    error.className = "error";
  } else {
    email.className = "invalid";
  }
});

// Здесь определяется поведение при попытке отправить данные
addEvent(form, "submit", function () {
  const test = email.value.length === 0 || emailRegExp.test(email.value);

  if (!test) {
    email.className = "invalid";
    error.textContent = "I expect an e-mail, darling!";
    error.className = "error active";

    // Некоторые устаревшие браузеры не поддерживают метод event.preventDefault()
    return false;
  } else {
    email.className = "valid";
    error.textContent = "";
    error.className = "error";
  }
});
```

Результат выглядит следующим образом:

Как вы можете видеть, сделать собственную валидацию не так уж и сложно. Сложность состоит лишь в том, чтобы сделать его кроссплатформенным и работающим с любой формой, которую можно создать. Для проверки формы доступно множество библиотек, например Validate.js.

Проверьте свои навыки!
----------------------

Вы дошли до конца этой статьи, но можете ли вы вспомнить самую важную информацию? Вы можете найти дополнительные тесты, чтобы убедиться, что вы сохранили эту информацию, прежде чем двигаться дальше — Test your skills: Form validation (en-US).

Заключение
----------

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

* Отображать явные сообщения об ошибках.
* Снисходительно относиться к формату ввода.
* Указывать, где именно возникла ошибка. Особенно в больших формах.

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

* Назад  (en-US)
* Обзор: Forms
* Далее
In this module
--------------

* Ваша первая форма (en-US)
* Как структурировать веб-формы
* Основные встроенные элементы формы (en-US)
* Типы полей ввода в HTML5 (en-US)
* Другие элементы формы (en-US)
* Стилизация веб-форм
* Продвинутая стилизация форм (en-US)
* UI псевдоклассы (en-US)
* Валидация форм на стороне клиента
* Отправка данных формы
### Продвинутые темы

* Как создавать кастомные элементы формы
* Отправка форм с помощью JavaScript
* Таблица совместимости CSS-свойств для элементов формы (en-US)


Отправка форм при помощи JavaScript - Изучение веб-разработки

Отправка форм при помощи JavaScript
===================================

HTML формы могут декларативно отправлять HTTP (en-US)-запросы. Но формы также могут подготовить HTTP-запросы для отправки с помощью JavaScript, например при помощи `XMLHttpRequest`. В этой статье исследуются подобные подходы.

Формы не всегда формы
---------------------

В современных веб-приложениях, одностраничных приложениях и приложениях на основе фреймворков, обычно HTML-формы (en-US) используются для отправки данных без загрузки нового документа при получении данных ответа. В начале поговорим о том почему это требует другого подхода.

### Получение контроля над глобальным интерфейсом

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

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

Асинхронная отправка произвольных данных обычно называется AJAX, что означает **"Asynchronous JavaScript And XML"** (Асинхронный JavaScript и XML).

### Чем он отличается?

Объект `XMLHttpRequest` (XHR) DOM может создавать HTTP-запросы, отправлять их, и получать их результат. Исторически, `XMLHttpRequest` был разработан для получения и отправки XML (en-US) в качестве формата обмена, который со временем был заменён на JSON (en-US). Но ни XML, ни JSON не вписываются в кодировку запроса данных формы. Данные формы (`application/x-www-form-urlencoded`) состоят из списка пар ключ/значение в кодировке URL. Для передачи бинарных данных, HTTP-запрос преобразуется в `multipart/form-data`.

**Примечание:** Сейчас Fetch API часто используется вместо XHR — это современная, обновлённая версия XHR, которая работает в похожем стиле, но имеет несколько преимуществ. Большая часть XHR-кода, которую вы увидите в этой статье можно заменить на Fetch.

Если вы управляете фронтендом (кодом, который выполняется в браузере) и бэкендом (кодом, который выполняется на стороне сервера), вы можете отправлять JSON/XML и обрабатывать их как хотите.

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

Так как нам следует отправлять подобные данные? Ниже описаны различные необходимые вам техники.

Отправка данных формы
---------------------

Есть три способа отправки данных формы:

* Создание `XMLHttpRequest` вручную.
* Использование самостоятельного `FormData` объекта.
* Использование `FormData` связанного с `<form>` элементом.

Давайте рассмотрим их подробнее:

### Создание XMLHttpRequest вручную

`XMLHttpRequest` это самый безопасный и надёжный способ создавать HTTP-запросы. Для отправки данных формы с помощью `XMLHttpRequest`, подготовьте данные с помощью URL-кодирования, и соблюдайте специфику запросов данных формы.

Посмотрите на пример:

```html
<button>Click Me!</button>
```

И на JavaScript:

```js
const btn = document.querySelector("button");

function sendData(data) {
  console.log("Sending data");

  const XHR = new XMLHttpRequest();

  let urlEncodedData = "",
    urlEncodedDataPairs = [],
    name;

  // Turn the data object into an array of URL-encoded key/value pairs.
  for (name in data) {
    urlEncodedDataPairs.push(
      encodeURIComponent(name) + "=" + encodeURIComponent(data[name]),
    );
  }

  // Combine the pairs into a single string and replace all %-encoded spaces to
  // the '+' character; matches the behaviour of browser form submissions.
  urlEncodedData = urlEncodedDataPairs.join("&").replace(/%20/g, "+");

  // Define what happens on successful data submission
  XHR.addEventListener("load", function (event) {
    alert("Yeah! Data sent and response loaded.");
  });

  // Define what happens in case of error
  XHR.addEventListener("error", function (event) {
    alert("Oops! Something went wrong.");
  });

  // Set up our request
  XHR.open("POST", "https://example.com/cors.php");

  // Add the required HTTP header for form data POST requests
  XHR.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

  // Finally, send our data.
  XHR.send(urlEncodedData);
}

btn.addEventListener("click", function () {
  sendData({ test: "ok" });
});
```

Это результат:

**Примечание:** This use of `XMLHttpRequest` is subject to the same-origin policy (en-US) if you want to send data to a third party web site. For cross-origin requests, you'll need CORS and HTTP access control (en-US).

### Using XMLHttpRequest and the FormData object

Building an HTTP request by hand can be overwhelming. Fortunately, the XMLHttpRequest specification provides a newer, simpler way to handle form data requests with the `FormData` (en-US) object.

The `FormData` (en-US) object can be used to build form data for transmission, or to get the data within a form element to manage how it's sent. Note that `FormData` (en-US) objects are "write only", which means you can change them, but not retrieve their contents.

Using this object is detailed in Using FormData Objects (en-US), but here are two examples:

#### Using a standalone FormData object

```html
<button>Click Me!</button>
```

You should be familiar with that HTML sample. Now for the JavaScript:

```js
const btn = document.querySelector("button");

function sendData(data) {
  const XHR = new XMLHttpRequest(),
    FD = new FormData();

  // Push our data into our FormData object
  for (name in data) {
    FD.append(name, data[name]);
  }

  // Define what happens on successful data submission
  XHR.addEventListener("load", function (event) {
    alert("Yeah! Data sent and response loaded.");
  });

  // Define what happens in case of error
  XHR.addEventListener(" error", function (event) {
    alert("Oops! Something went wrong.");
  });

  // Set up our request
  XHR.open("POST", "https://example.com/cors.php");

  // Send our FormData object; HTTP headers are set automatically
  XHR.send(FD);
}

btn.addEventListener("click", function () {
  sendData({ test: "ok" });
});
```

Here's the live result:

#### Using FormData bound to a form element

You can also bind a `FormData` object to an `<form>` element. This creates a `FormData` object that represents the data contained in the form.

The HTML is typical:

```html
<form id="myForm">
  <label for="myName">Send me your name:</label>
  <input id="myName" name="name" value="John" />
  <input type="submit" value="Send Me!" />
</form>
```

But JavaScript takes over the form:

```js
window.addEventListener("load", function () {
  function sendData() {
    const XHR = new XMLHttpRequest();

    // Bind the FormData object and the form element
    const FD = new FormData(form);

    // Define what happens on successful data submission
    XHR.addEventListener("load", function (event) {
      alert(event.target.responseText);
    });

    // Define what happens in case of error
    XHR.addEventListener("error", function (event) {
      alert("Oops! Something went wrong.");
    });

    // Set up our request
    XHR.open("POST", "https://example.com/cors.php");

    // The data sent is what the user provided in the form
    XHR.send(FD);
  }

  // Access the form element...
  const form = document.getElementById("myForm");

  // ...and take over its submit event.
  form.addEventListener("submit", function (event) {
    event.preventDefault();

    sendData();
  });
});
```

Here's the live result:

You can even get more involved with the process by using the form's `elements` property to get a list of all of the data elements in the form and manually manage them one at a time. To learn more about that, see the example in Accessing form controls.

Dealing with binary data
------------------------

If you use a `FormData` (en-US) object with a form that includes `<input type="file">` widgets, the data will be processed automatically. But to send binary data by hand, there's extra work to do.

There are many sources for binary data, including `FileReader`, `Canvas`, and WebRTC (en-US). Unfortunately, some legacy browsers can't access binary data or require complicated workarounds. To learn more about the `FileReader` API, see Using files from web applications.

The least complicated way of sending binary data is by using `FormData` (en-US)'s `append()` method, demonstrated above. If you have to do it by hand, it's trickier.

In the following example, we use the `FileReader` API to access binary data and then build the multi-part form data request by hand:

```html
<form id="theForm">
  <p>
    <label for="theText">text data:</label>
    <input id="theText" name="myText" value="Some text data" type="text" />
  </p>
  <p>
    <label for="theFile">file data:</label>
    <input id="theFile" name="myFile" type="file" />
  </p>
  <button>Send Me!</button>
</form>
```

As you see, the HTML is a standard `<form>`. There's nothing magical going on. The "magic" is in the JavaScript:

```js
// Because we want to access DOM nodes,
// we initialize our script at page load.
window.addEventListener("load", function () {
  // These variables are used to store the form data
  const text = document.getElementById("theText");
  const file = {
    dom: document.getElementById("theFile"),
    binary: null,
  };

  // Use the FileReader API to access file content
  const reader = new FileReader();

  // Because FileReader is asynchronous, store its
  // result when it finishes to read the file
  reader.addEventListener("load", function () {
    file.binary = reader.result;
  });

  // At page load, if a file is already selected, read it.
  if (file.dom.files[0]) {
    reader.readAsBinaryString(file.dom.files[0]);
  }

  // If not, read the file once the user selects it.
  file.dom.addEventListener("change", function () {
    if (reader.readyState === FileReader.LOADING) {
      reader.abort();
    }

    reader.readAsBinaryString(file.dom.files[0]);
  });

  // sendData is our main function
  function sendData() {
    // If there is a selected file, wait it is read
    // If there is not, delay the execution of the function
    if (!file.binary && file.dom.files.length > 0) {
      setTimeout(sendData, 10);
      return;
    }

    // To construct our multipart form data request,
    // We need an XMLHttpRequest instance
    const XHR = new XMLHttpRequest();

    // We need a separator to define each part of the request
    const boundary = "blob";

    // Store our body request in a string.
    let data = "";

    // So, if the user has selected a file
    if (file.dom.files[0]) {
      // Start a new part in our body's request
      data += "--" + boundary + "\r\n";

      // Describe it as form data
      data +=
        "content-disposition: form-data; " +
        // Define the name of the form data
        'name="' +
        file.dom.name +
        '"; ' +
        // Provide the real name of the file
        'filename="' +
        file.dom.files[0].name +
        '"\r\n';
      // And the MIME type of the file
      data += "Content-Type: " + file.dom.files[0].type + "\r\n";

      // There's a blank line between the metadata and the data
      data += "\r\n";

      // Append the binary data to our body's request
      data += file.binary + "\r\n";
    }

    // Text data is simpler
    // Start a new part in our body's request
    data += "--" + boundary + "\r\n";

    // Say it's form data, and name it
    data += 'content-disposition: form-data; name="' + text.name + '"\r\n';
    // There's a blank line between the metadata and the data
    data += "\r\n";

    // Append the text data to our body's request
    data += text.value + "\r\n";

    // Once we are done, "close" the body's request
    data += "--" + boundary + "--";

    // Define what happens on successful data submission
    XHR.addEventListener("load", function (event) {
      alert("Yeah! Data sent and response loaded.");
    });

    // Define what happens in case of error
    XHR.addEventListener("error", function (event) {
      alert("Oops! Something went wrong.");
    });

    // Set up our request
    XHR.open("POST", "https://example.com/cors.php");

    // Add the required HTTP header to handle a multipart form data POST request
    XHR.setRequestHeader(
      "Content-Type",
      "multipart/form-data; boundary=" + boundary,
    );

    // And finally, send our data.
    XHR.send(data);
  }

  // Access our form...
  const form = document.getElementById("theForm");

  // ...to take over the submit event
  form.addEventListener("submit", function (event) {
    event.preventDefault();
    sendData();
  });
});
```

Here's the live result:

Conclusion
----------

Depending on the browser and the type of data you are dealing with, sending form data through JavaScript can be easy or difficult. The `FormData` (en-US) object is generally the answer, and you can use a polyfill for it on legacy browsers.

See also
--------

### Learning path

* Your first HTML form (en-US)
* How to structure an HTML form
* The native form widgets (en-US)
* HTML5 input types (en-US)
* Additional form controls (en-US)
* UI pseudo-classes (en-US)
* Styling HTML forms
* Form data validation (en-US)
* Sending form data (en-US)
### Advanced Topics

* Sending forms through JavaScript
* How to build custom form widgets
* HTML forms in legacy browsers (en-US)
* Advanced styling for HTML forms (en-US)
* Property compatibility table for form widgets (en-US)