В этой статье разберём все элементы форм HTML, атрибуты валидации, стилизацию через CSS, доступность, безопасность и готовые примеры кода. Подсказка в поле ввода задаётся атрибутом placeholder — об этом отдельный раздел.
Не знаете, с чего ребёнку начать в IT?
Дайте попробовать разные направления: Python, веб-разработку и робототехнику
Тег <form> создаёт область, которая объединяет элементы ввода и определяет поведение при отправке данных. Сам тег не отображается на странице — это невидимый контейнер. Без него браузер не знает, куда отправить данные, каким методом и в каком формате.
Простейшая HTML-форма выглядит так:
<form action=»/submit.php» method=»post»>
<input type=»text» name=»username» placeholder=»Ваше имя»>
<button type=»submit»>Отправить</button>
</form>
Даже такая минимальная конструкция полностью работоспособна: принимает данные и отправляет их на сервер по пути /submit.php.
Хотите освоить веб-разработку с нуля — включая HTML-формы, JavaScript и Flask? На курсе «Программирование: Уверенный старт» школьники с первого урока создают реальные проекты: TO-DO List на HTML/CSS/JS, веб-приложение с обработкой форм на Flask и Telegram-бота на Python. Обучение бесплатное — 36 часов онлайн, совмещается со школой. Узнайте подробнее на странице курса.
Тег <form> поддерживает семь основных атрибутов:
| Атрибут |
Описание |
Значение по умолчанию |
|---|---|---|
| action | URL обработчика данных | Текущая страница |
| method | HTTP-метод: GET или POST | GET |
| enctype | Формат кодирования данных | application/x-www-form-urlencoded |
| autocomplete | Автозаполнение браузером | on |
| novalidate | Отключает HTML5-валидацию | — |
| target | Куда открыть ответ (_self, _blank) | _self |
| name | Имя формы для JavaScript | — |
Атрибуты тега <form>. Источник: спецификация HTML5, W3C.
enctype критичен для загрузки файлов: значение multipart/form-data обязательно — без него содержимое файлов не попадёт в запрос. По умолчанию используется application/x-www-form-urlencoded, который подходит для текстовых данных.
novalidate отключает встроенную HTML5-валидацию и передаёт контроль JavaScript — нужно, когда требуются кастомные сообщения об ошибках с нужным дизайном.
Пример формы с явными атрибутами:
<form action=»/register.php» method=»post»
enctype=»multipart/form-data»
autocomplete=»on» novalidate>
<!— поля формы —>
</form>
Выбор метода напрямую влияет на безопасность и поведение формы. GET добавляет данные к URL, POST скрывает их в теле запроса.
| Параметр |
GET |
POST |
|---|---|---|
| Видимость данных | В URL: ?name=value | В теле запроса |
| Лимит объёма | ~2 000 символов | Без ограничений |
| Кэширование | Да | Нет |
| Безопасность | Низкая (данные в URL) | Выше |
| Поддержка файлов | Нет | Да |
| Типичные кейсы | Поиск, фильтры, пагинация | Регистрация, авторизация, заказы |
GET: данные видны в адресной строке, сохраняются в истории браузера и серверных логах. Использовать GET для паролей — критическая ошибка: пароль окажется в URL.
POST: данные в теле запроса, не отображаются в адресной строке, поддерживают файлы. Для паролей, персональных данных и платёжных форм — POST плюс HTTPS обязательны.
Устаревший подход action=»mailto:» отправляет данные в почтовый клиент пользователя — ненадёжно и непредсказуемо. Для отправки писем используйте серверную обработку: PHP mail() или SMTP-сервис.
Внутри тега <form> размещают восемь основных элементов: <input> (поле ввода), <textarea> (многострочный текст), <select> (выпадающий список), <button> (кнопки), <label> (подписи), <fieldset> (группировка), <legend> (заголовок группы) и <datalist> (список подсказок). Главное правило: каждый интерактивный элемент формы должен иметь атрибут name — без него браузер не включит значение поля в запрос при отправке.
<input> — самозакрывающийся и самый универсальный элемент форм HTML. Атрибут type полностью меняет поведение, внешний вид и тип клавиатуры на мобильных устройствах.
| Тип |
Назначение |
Спецатрибуты |
Мобильная клавиатура |
|---|---|---|---|
| text | Однострочный текст | maxlength, pattern | Стандартная |
| Адрес электронной почты | multiple | С @ и .com | |
| password | Пароль (скрывает символы) | minlength | Стандартная |
| number | Число | min, max, step | Цифровая |
| tel | Номер телефона | pattern | Цифровая с + |
| url | Адрес сайта | pattern | С / и .com |
| search | Строка поиска | — | С кнопкой поиска |
| date | Дата | min, max | Календарь |
| time | Время | min, max, step | Колёсико времени |
| datetime-local | Дата и время локальные | min, max | Дата + время |
| checkbox | Флажок (можно несколько) | checked | — |
| radio | Радиокнопка (один из группы) | checked | — |
| range | Ползунок | min, max, step | — |
| color | Палитра цвета | — | — |
| file | Загрузка файла | accept, multiple | — |
| hidden | Скрытое поле | — | — |
| submit | Кнопка отправки формы | value | — |
| reset | Кнопка сброса всех полей | value | — |
| image | Кнопка-изображение | src, alt, width | — |
| button | Кнопка без действия по умолчанию | — | — |
Радиокнопки объединяются в группу через одинаковый атрибут name — браузер разрешает выбрать только один вариант из группы. Атрибут checked задаёт предвыбранный вариант.
<input type=»hidden»> невидим пользователю, но участвует в отправке данных. Применяется для CSRF-токенов, идентификаторов сессии и технических параметров. Если у <input> нет атрибута name — данные не попадут на сервер, даже если поле заполнено.
<textarea> — парный тег для многострочного текста. Атрибут rows задаёт высоту (количество строк), cols — ширину. maxlength ограничивает длину, wrap управляет переносами. Начальное значение помещается между тегами, а не в атрибут value. Размер текстовой области пользователь может менять мышью — управляйте этим через CSS-свойство resize.
<select> создаёт выпадающий список. Атрибут multiple позволяет выбрать несколько значений (добавьте [] к имени: name=»skills[]»). <optgroup> объединяет опции в группы с общим заголовком. Атрибут selected на <option> задаёт значение по умолчанию.
<datalist> отличается от <select>: пользователь вводит текст свободно, а список — только подсказки. Связывается с полем через атрибут list:
<input list=»langs» name=»language» placeholder=»Язык»>
<datalist id=»langs»>
<option value=»Python»>
<option value=»JavaScript»>
<option value=»HTML»>
</datalist>
<button> имеет три типа: submit — отправляет форму (поведение по умолчанию), reset — очищает все поля, button — без встроенного действия, работает только через JavaScript.

<label> связывает текстовую подпись с полем ввода. Два способа привязки:
Явная привязка предпочтительна — надёжнее работает с вспомогательными технологиями (AT). UX-эффект: клик по тексту label переводит фокус в связанное поле, что увеличивает область нажатия и улучшает опыт на мобильных устройствах.
<!— Явная привязка —>
<label for=»email»>Email</label>
<input type=»email» id=»email» name=»email»>
<!— Неявная привязка —>
<label>Email <input type=»email» name=»email»></label>
<fieldset> логически группирует несколько полей и визуально отделяет их рамкой. Атрибут disabled на <fieldset> отключает все вложенные поля одновременно — удобно для многошаговых форм.
<legend> — заголовок группы внутри <fieldset>. Скринридер озвучивает его перед каждым полем группы, давая пользователю пространственный контекст. Нативная стилизация <legend> нередко сложна — популярный паттерн: скрыть legend через класс visually-hidden и отобразить видимый заголовок через <h3> рядом.
Подсказка в поле ввода HTML-формы задаётся атрибутом placeholder. Синтаксис: <input placeholder=»Введите email»>. Текст отображается до начала ввода и исчезает, когда пользователь начинает писать. Стилизация через CSS: ::placeholder { color: #aaa; }. Важно: placeholder не заменяет <label> — при фокусе подсказка пропадает, скринридеры читают его непоследовательно.
Остальные ключевые атрибуты полей:
required — поле обязательно к заполнению; браузер не отправит форму и покажет встроенное сообщение об ошибке.
pattern — RegEx (регулярное выражение) для проверки формата. Атрибут title задаёт текст подсказки при несовпадении:
<input type=»text» name=»phone»
pattern=»[0-9]{10}» title=»Введите 10 цифр»>
min / max — допустимый диапазон для полей number, date, time, range.
minlength / maxlength — минимальная и максимальная длина текста.
autocomplete — управляет автозаполнением; значения: on, off или конкретные подсказки браузеру (email, username, current-password).
autofocus — поле получает фокус при загрузке страницы. Используйте один раз на форму.
disabled — поле неактивно, данные не отправляются. readonly — поле нельзя редактировать, но данные участвуют в отправке — главное отличие от disabled.
Надёжная валидация данных строится в три слоя: HTML5 (базовая проверка без JavaScript), JavaScript (гибкость и кастомные сообщения) и серверная проверка (безопасность). Каждый слой решает свою задачу и не заменяет другие — нужны все три.
Нативная валидация работает без единой строки JavaScript. Браузер автоматически проверяет поля перед отправкой и показывает встроенные сообщения об ошибках, локализованные под язык системы пользователя.
Атрибуты нативной валидации:
novalidate на теге <form> отключает браузерную валидацию полностью — используется, когда нужен кастомный UI ошибок через JavaScript.
Главное ограничение: HTML5-валидацию обходят за секунды — через DevTools, Postman или прямой HTTP-запрос. Серверная проверка остаётся обязательной в любом случае.
JavaScript даёт полный контроль над логикой проверки и текстами ошибок. Стандартный паттерн — добавить novalidate на форму, перехватить событие submit и проверить поля самостоятельно:
form.addEventListener(‘submit’, e => {
e.preventDefault();
if (!form.checkValidity()) {
// показываем кастомные ошибки
}
});
Constraint Validation API (программный интерфейс ограничительной валидации) предоставляет:
Объект validity содержит флаги: valueMissing, patternMismatch, rangeOverflow, tooLong — каждый соответствует отдельному виду нарушения. При показе ошибки добавьте к полю aria-invalid=»true» и aria-describedby — ссылку на элемент с сообщением об ошибке.
Клиентскую валидацию обходят элементарно: через DevTools, Postman или curl. Сервер обязан перепроверять все входящие данные самостоятельно — принцип «Never trust user input» (никогда не доверяй вводу пользователя).
PHP:
$email = filter_var($_POST[’email’], FILTER_VALIDATE_EMAIL);
$name = htmlspecialchars(strip_tags($_POST[‘name’]));
Python:
import html
name = html.escape(request.form.get(‘name’, »))
Правило простое: серверная валидация = безопасность; клиентская = удобство пользователя. Они решают разные задачи и нужны оба.

Доступность форм нужна не только пользователям скринридеров (JAWS, NVDA, VoiceOver) — она улучшает опыт всех, кто работает с клавиатурой или мобильным устройством.
Обязательные практики:
<label> с атрибутами for/id — обязателен для каждого поля. Без него скринридер не озвучит назначение поля при фокусе, и пользователь не сможет понять, что вводить.
<fieldset> + <legend> — вспомогательные технологии (AT) озвучивают текст legend перед каждым полем группы, давая контекст: «Личные данные: Имя».
ARIA-атрибуты (стандарт WAI-ARIA, W3C):
Принцип WCAG 2.1: ARIA дополняет семантический HTML, а не заменяет его. aria-required не компенсирует отсутствующий <label>.
Навигация с клавиатуры: tabindex управляет порядком фокуса. Нативный HTML-порядок чаще оптимален — злоупотреблять tabindex не стоит. Критично: никогда не убирайте стиль :focus из CSS без замены — он единственный визуальный ориентир для пользователей клавиатуры и напрямую влияет на соответствие WCAG 2.1.

Базовые CSS-свойства для полей ввода:
input, textarea, select {
width: 100%;
padding: 10px 14px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 16px;
font-family: inherit;
box-sizing: border-box;
}
Ребенок хочет изучать программирование? Пусть начнёт с реальных проектов
На курсе школьники создают Telegram-бота, сайт и веб-приложение
Псевдоклассы состояния:
Псевдоклассы валидации:
input:valid { border-color: #28a745; }
input:invalid:not(:focus):not(:placeholder-shown) {
border-color: #dc3545;
}
::placeholder стилизует текст-подсказку:
::placeholder { color: #aaa; font-style: italic; }
Адаптивность: при ширине экрана менее 600 пикселей задавайте font-size: 16px — меньший размер вызывает автоматический зум на iOS и ломает вёрстку.
Flexbox (гибкий контейнер) для вертикальной раскладки полей:
form { display: flex; flex-direction: column; gap: 16px; max-width: 480px; }
Нативная стилизация <select>, <checkbox> и <radio> ограничена — браузеры рендерят их по-разному. Распространённый паттерн: скрыть нативный элемент через appearance: none и создать кастомный интерфейс поверх.
Три типовых примера из практики веб-разработки: форма обратной связи, форма регистрации и форма загрузки файлов. Каждый пример — рабочий HTML-код с разбором ключевых решений.
<form method=»post» action=»/contact.php» novalidate>
<fieldset>
<legend>Напишите нам</legend>
<label for=»cf-name»>Имя</label>
<input type=»text» id=»cf-name» name=»name»
required placeholder=»Иван Иванов»>
<label for=»cf-email»>Email</label>
<input type=»email» id=»cf-email» name=»email»
required placeholder=»ivan@example.com»>
<label for=»cf-msg»>Сообщение</label>
<textarea id=»cf-msg» name=»message» rows=»5″
required placeholder=»Ваш вопрос…»></textarea>
<button type=»submit»>Отправить</button>
</fieldset>
</form>
<fieldset> с <legend> группирует поля и даёт контекст скринридерам. Каждое поле явно привязано к <label> через for и id. required блокирует отправку пустой формы.
Для отправки данных формы HTML на почту используйте PHP mail() или SMTP-сервис — не action=»mailto:». Последний зависит от почтового клиента пользователя и работает непредсказуемо.
Защита от спама: добавьте Google reCAPTCHA или Honeypot-поле (поле-ловушку) — скрытое поле, которое боты заполняют, а люди не видят:
<input type=»text» name=»trap» style=»display:none» tabindex=»-1″>
На сервере: если trap не пустое — запрос от бота. Всегда санитизируйте данные через htmlspecialchars() перед выводом.
<form method=»post» action=»/register.php» novalidate>
<input type=»hidden» name=»csrf_token» value=»<?= $token ?>»>
<fieldset>
<legend>Личные данные</legend>
<label for=»r-name»>Имя</label>
<input type=»text» id=»r-name» name=»name» required>
<label for=»r-email»>Email</label>
<input type=»email» id=»r-email» name=»email» required>
</fieldset>
<fieldset>
<legend>Аккаунт</legend>
<label for=»r-user»>Логин</label>
<input type=»text» id=»r-user» name=»username»
pattern=»[a-zA-Z0-9_]+» required
title=»Только буквы, цифры и _»>
<label for=»r-pass»>Пароль</label>
<input type=»password» id=»r-pass» name=»password»
minlength=»8″ required>
<label for=»r-pass2″>Повторите пароль</label>
<input type=»password» id=»r-pass2″ name=»confirm»>
</fieldset>
<button type=»submit»>Зарегистрироваться</button>
</form>
Два <fieldset> — «Личные данные» и «Аккаунт» — логически делят форму регистрации. <input type=»hidden»> передаёт CSRF-токен для защиты от межсайтовой подделки запросов.
JavaScript проверяет совпадение паролей через setCustomValidity():
const pass = document.getElementById(‘r-pass’);
const pass2 = document.getElementById(‘r-pass2’);
pass2.addEventListener(‘input’, () => {
pass2.setCustomValidity(
pass.value !== pass2.value ? ‘Пароли не совпадают’ : »
);
});
CSS-состояния дают визуальную обратную связь: input:valid { border-color: green; }, input:invalid { border-color: red; }. Форма должна работать только по HTTPS.
<form method=»post» action=»/upload.php»
enctype=»multipart/form-data»>
<label for=»doc»>Загрузить документ</label>
<input type=»file» id=»doc» name=»doc»
accept=».pdf,.docx» multiple>
<button type=»submit»>Загрузить</button>
</form>
Обязательно: enctype=»multipart/form-data» на теге <form>. Без него браузер отправит только имя файла, а не его содержимое — файлы не загрузятся.
accept=».pdf,.docx» ограничивает допустимые MIME-типы (форматы файлов); multiple позволяет выбрать несколько файлов одновременно. На сервере файл доступен через $_FILES[‘doc’] в PHP.
Типичная ошибка: забыть enctype при переносе формы из другого проекта — поле $_FILES окажется пустым, а причина неочевидна при отладке.

HTML-формы — главная точка входа пользовательских данных и основная цель веб-атак. Четыре ключевые угрозы и способы защиты:
XSS (межсайтовый скриптинг): злоумышленник вводит в поле вредоносный JavaScript-код, который выполняется в браузере других пользователей. Защита: санитизация данных перед выводом — htmlspecialchars() в PHP, html.escape() в Python. Дополнительно: заголовок Content Security Policy (политика безопасности контента, CSP) ограничивает выполнение скриптов на странице.
CSRF (подделка межсайтовых запросов): вредоносный сайт вынуждает авторизованного пользователя отправить запрос к другому сервису. Защита: скрытое поле с уникальным токеном, который проверяется на сервере при каждой отправке:
<input type=»hidden» name=»csrf_token» value=»уникальный_токен»>
SQL-инъекция: вредоносный SQL-код через поля формы изменяет запросы к базе данных. Защита: подготовленные запросы (prepared statements) вместо склейки строк; filter_var() в PHP.
Боты и спам: автоматические отправки формы. Защита: reCAPTCHA от Google, Honeypot-поле-ловушка, Rate Limiting (ограничение частоты запросов с одного IP за промежуток времени).
HTTPS обязателен для любой формы с личными данными. Без него содержимое POST-запроса передаётся открытым текстом — перехватить данные может любой участник сети на пути от браузера до сервера.
Динамические формы изменяются в реальном времени без перезагрузки страницы — через JavaScript и DOM (Document Object Model, объектную модель документа).
Базовые паттерны:
Шесть типичных ошибок при работе с формами HTML:
Подсказка задаётся атрибутом placeholder. Синтаксис: <input placeholder=»Введите email»>. Текст отображается до начала ввода и исчезает, когда пользователь начинает писать. Стилизация через CSS ::placeholder. Важно: placeholder не заменяет <label> — при фокусе подсказка пропадает, что создаёт барьер для пользователей скринридеров и людей с когнитивными особенностями.
GET добавляет данные к URL (?name=value): виден в адресной строке, кэшируется, лимит около 2 000 символов. POST скрывает данные в теле запроса: не виден в URL, без ограничений объёма, поддерживает файлы. Правило: GET — для поиска и фильтров; POST — для паролей, регистрации, заказов и любых персональных данных.
Чаще всего причина — отсутствие атрибута name у поля. Без name браузер не включает поле в запрос. Проверьте каждый <input>, <textarea>, <select>. Вторая частая причина — для загрузки файлов не указан enctype=»multipart/form-data» на теге <form>.
Структура: <form method=»post» action=»/contact.php»> с полями для имени (<input type=»text» name=»name» required>), адреса электронной почты (<input type=»email» name=»email» required>), сообщения (<textarea name=»message»>) и кнопкой submit. Каждое поле оберните в <label>. Для защиты от спама добавьте reCAPTCHA. Данные обработайте через PHP mail() или SMTP-сервис.
Используйте <fieldset> — создаёт логическую группу полей с визуальной рамкой. Внутри добавьте <legend> — заголовок группы, который скринридер озвучит перед каждым полем. disabled на <fieldset> отключает все вложенные поля одновременно. Форму регистрации, например, делите на «Личные данные» и «Настройки аккаунта».
Обязательно добавьте enctype=»multipart/form-data» к тегу <form> и <input type=»file» name=»doc» accept=».pdf,.docx»>. Без enctype файл не придёт на сервер — браузер отправит только имя. Атрибут accept ограничивает допустимые типы; multiple позволяет выбрать несколько файлов. На сервере файл доступен через $_FILES[‘doc’] в PHP.
Радиокнопки создаются через <input type=»radio» name=»group» value=»option1″>. Все кнопки одной группы должны иметь одинаковый name — браузер разрешает выбрать только один вариант. Атрибут checked задаёт предвыбранный вариант. Каждую кнопку сопровождайте <label for=»id»> для доступности и удобства нажатия.
От XSS: санитизируйте входящие данные перед выводом — htmlspecialchars() в PHP, html.escape() в Python. От CSRF: добавьте <input type=»hidden» name=»csrf_token» value=»…»> и проверяйте токен на сервере при каждой отправке. Используйте HTTPS — без него данные передаются открытым текстом. Клиентская защита — это удобство; серверная — безопасность.
Динамическая форма изменяется в реальном времени через JavaScript: поля показываются и скрываются в зависимости от выбора пользователя (element.style.display), каскадные <select> обновляют варианты при смене родительского значения, новые поля добавляются через appendChild(). Применяется в многошаговых формах, фильтрах каталога, конфигураторах продуктов.
HTML-формы — часть базового курса веб-разработки. Начните с тега <form> и его атрибутов, затем разберите типы <input>, валидацию и стилизацию через CSS. На курсе «Программирование: Уверенный старт» (бесплатное обучение, 36 часов) HTML-формы изучаются в Модуле 2 на практике: вы создаёте TO-DO List и позднее подключаете Flask-формы с POST/GET-запросами.
Пусть ребёнок сделает первый IT-проект уже в этом месяце
Бесплатный онлайн-курс с понятной программой, поддержкой и проектами для портфолио