Элементы формы — это обычные элементы на веб-страницах, с помощью которых пользователи могут что-то вводить или выбирать параметры. Наиболее распространенными сценариями использования элементов формы являются регистрация, вход в систему и оплата. В этой статье мы рассмотрим, как сделать формы более удобными для пользователя.
🇨🇳 Чтобы прочитать эту статью на китайском языке, пожалуйста, посетите мой Zhihu. 文章的中文版请戳我的知乎。
1. Основы элементов формы
🔍 1.1 Элементы формы
Элемент <form>
может содержать один или несколько элементов формы:
<input>
: Тег<input>
создает интерактивные элементы управления для ввода данных пользователями. Наиболее распространенными значениями атрибутаtype
являютсяtext
,checkbox
,file
,number
,password
,radio
,submit
иreset
.<button>
: Тег<button>
представляет кнопку, на которую можно нажать.<textarea>
: Тег<textarea>
представляет собой многострочный элемент управления для редактирования обычного текста.<select>
и<option>
: Тег<select>
представляет собой элемент управления, предоставляющий раскрывающийся список параметров. Выбранный параметр установит для атрибутаselected
определенного элемента<option>
значениеtrue
.<datalist>
и<option>
: Тег<datalist>
содержит набор элементов<option>
, представляющих допустимые или рекомендуемые параметры, доступные для выбора. Чтобы привязать элемент<datalist>
к определенному элементу<input>
, атрибутlist
элемента<input>
должен совпадать с атрибутомid
элемента<datalist>
.<option>
и<optgroup>
: тег<option>
определяет параметр для выбора, а тег<optgroup>
группирует набор элементов<option>
в элементе<select>
.<label>
: Тег<label>
представляет заголовок для определенных элементов формы. Чтобы связать элемент<label>
и элемент формы вместе, атрибутfor
элемента<label>
должен совпадать с атрибутомid
элемента формы.<fieldset>
и<legend>
: Тег<fieldset>
объединяет несколько элементов управления, а также метки внутри формы. Тег<legend>
представляет собой заголовок содержимого родительского тега<fieldset>
.<meter>
и<progress>
: Тег<meter>
представляет либо дробное значение, либо скалярное значение в пределах известного диапазона. Тег<progress>
отображает индикатор (обычно индикатор выполнения), показывающий ход выполнения задачи.<output>
: Тег<output>
представляет результат вычисления (входные значения вычисления определяются разделенным пробелом списком других элементовid
в атрибутеfor
) или результат действия пользователя.
💻 Для примера кода об элементах формы вы можете посетить мои CodePen — ‹input› и CodePen — FormElements.
Вы можете посетить W3Schools — HTML Form Elements, чтобы получить более полное представление об элементах формы.
🔍 1.2 События формы
Мы часто прослушиваем некоторые события для обработки формы. Вот некоторые общие события, которые мы используем:
focus
: событиеfocus
срабатывает, когда элемент получает фокус, например. нажав на элемент<input>
.blur
: событиеblur
срабатывает, когда элемент теряет фокус.input
: событиеinput
возникает, когда изменяетсяvalue
элемента<input>
,<select>
или<textarea>
.change
: событиеchange
срабатывает при измененииvalue
элемента<input>
,<select>
или<textarea>
. Он запускается, когда элемент<input type="checkbox">
отмечен/не отмечен, элемент<input type="radio">
проверен, пользователь явно фиксирует изменение значения (например, выбрав значение из раскрывающегося списка<select>
) или какой-либо определенный элемент формы (например,<textarea>
,<input type="text">
) теряет фокус после изменения его значения.click
: событиеclick
срабатывает, когда пользователь использует указывающее устройство (например, мышь) для одновременного нажатия и отпускания элемента.contextmenu
: событиеcontextmenu
срабатывает, когда пользователь щелкает элемент правой кнопкой мыши.select
: событиеselect
срабатывает, когда какой-либо текст был выделен (т. е. пользователь использует указывающее устройство, чтобы выделить какой-либо текст).reset
: событиеreset
срабатывает при сбросе элементаform
.submit
: событиеsubmit
срабатывает при отправке элементаform
.
💻 Для примера кода о событиях формы вы можете посетить мой CodePen.
1.3 Объединение элементов формы и событий формы
Теперь давайте объединим элементы формы и события формы вместе, чтобы увидеть, как работает форма.
<form id="hobby-form"> <label for="first-name">First Name: </label> <input type="text" name="first-name" id="first-name" /> <br/> <label for="last-name">Last Name: </label> <input type="text" name="last-name" id="last-name" /> <br/> <fieldset> <legend>Gender: </legend> <input type="radio" name="gender" id="gender-female" value="F" /> <label for="gender-female">Female</label> <input type="radio" name="gender" id="gender-male" value="M" /> <label for="gender-male">Male</label> </fieldset> <label for="drink">Favourite Drink: </label> <select name="drink" id="drink"> <option value="">--Please choose an option--</option> <option value="Coffee">Coffee</option> <option value="Milk Tea">Milk Tea</option> <option value="Soft Drink">Soft Drink (Coke, Sprite, etc.)</option> <option value="Water">Water</option> </select> <br/> <label for="hobby">Hobby: </label> <input type="text" name="hobby" id="hobby" list="hobby-recommendations" /> <datalist id="hobby-recommendations"> <option value="Photography" /> <option value="Basketball" /> <option value="Watching TV" /> </datalist> <br/> <button type="submit">Submit</button> </form> let firstName; let lastName; let gender; let drink; let hobby; const form = document.querySelector('#hobby-form'); // Change the variable value with its corresponding user input form.addEventListener('input', (event) => { switch(event.target.name) { case 'first-name': firstName = event.target.value.trim(); break; case 'last-name': lastName = event.target.value.trim(); break; case 'gender': gender = event.target.value; break; case 'drink': drink = event.target.value; break; case 'hobby': hobby = event.target.value.trim(); break; default: break; } }); // Form validation after submission form.addEventListener('submit', (event) => { event.preventDefault(); const emptyFields = []; if (!firstName) { emptyFields.push(' first name'); } if (!lastName) { emptyFields.push(' last name'); } if (!gender) { emptyFields.push(' gender'); } if (!drink) { emptyFields.push(' favourite drink'); } if (!hobby) { emptyFields.push(' hobby'); } if (emptyFields.length) { alert(`You need to fill in your${emptyFields.toString()}`); } else { alert(`Hello ${firstName} ${lastName} (${gender}), you like ${drink} and ${hobby}!`); firstName = ''; lastName = ''; gender = ''; drink = ''; hobby = ''; form.reset(); } });
💻 Приведенный выше пример кода также можно найти в моем CodePen.
2. Проверка формы
Когда мы пишем какой-либо код, мы не можем предполагать, что пользователи предоставят разумные и достоверные данные, поэтому мы должны помнить о том, что «пользовательские данные не заслуживают доверия».
🔍 2.1 Встроенная проверка формы
Элементы формы предоставляют некоторые атрибуты для проверки пользовательских данных, не полагаясь на JavaScript.
type
: Атрибутtype
тега<input>
указывает, что данные должны бытьtext
,number
,email
или любым другим определенным типом предустановки.required
: Атрибутrequired
тега<input>
,<textarea>
или<select>
указывает, что поле формы должно быть заполнено перед отправкой формы.minlength
иmaxlength
: Атрибутыminlength
иmaxlength
тегов<input>
и<textarea>
определяют минимальную и максимальную длину входной строки.min
иmax
: Атрибутыmin
иmax
тега<input>
определяют минимальное и максимальное значения числовых типов ввода.pattern
: атрибутpattern
тега<input>
задает регулярное выражение, определяющее шаблон, которому должны следовать входные данные.
💻 Для примера кода встроенной проверки формы посетите мой CodePen.
💡 2.2 После подтверждения отправки
Когда пользователь предоставляет все данные и отправляет форму, обычно нажав кнопку, информация отправляется на сервер и проверяется. Ответ «валидатора» отправляется обратно на компьютер пользователя и визуализируется либо в виде подтверждающего сообщения («все прошло нормально!»), либо в виде набора сообщений об ошибках.
Пример кода в части 1.3 и встроенная проверка формы в части 2.1 выполняются после проверки отправки.
Плюсы ✅:
- Плывите по течению. После отправки проверка уважает концентрацию пользователя, воздерживаясь от бомбардировки его исправлениями.
- Пользователи сосредоточены на режиме завершения, и после нажатия кнопки отправки они вводят исправление ошибок, что повышает их внимание к деталям.
- Все входные данные проверяются на сервере, поэтому проверка непротиворечива.
Минусы ❌:
- Разочарование в пакете. Получение всех ошибок сразу может быть ошеломляющим для пользователя, особенно если они накапливаются в блоке вверху или внизу формы.
- Снизить скорость выполнения. Разочарование может привести к более высокому отсеву.
💡 2.3 Встроенная проверка
Сообщения проверки отображаются сразу после того, как пользователь вводит данные для формирования полей. Обычно информация отображается рядом с полями и побуждает пользователя к немедленным действиям.
<form id="hobby-form"> <label for="first-name">First Name: </label> <input type="text" name="first-name" id="first-name" /> <span id="first-name-output"></span> <br/> <label for="last-name">Last Name: </label> <input type="text" name="last-name" id="last-name" /> <span id="last-name-output"></span> <br/> <label for="email">Email: </label> <input type="email" name="email" id="email" /> <span id="email-output"></span> <br/> <label for="hobby">Hobby: </label> <input type="text" name="hobby" id="hobby" /> <span id="hobby-output"></span> <br/> <button type="submit">Submit</button> </form> let firstName; let lastName; let email; let hobby; const firstNameOutput = document.querySelector('#first-name-output'); document.querySelector('#first-name').addEventListener('input', (event) => { firstName = event.target.value.trim(); firstNameOutput.innerHTML = firstName ? '✔️' : '✖️ Please enter your first name'; }); const lastNameOutput = document.querySelector('#last-name-output'); document.querySelector('#last-name').addEventListener('input', (event) => { lastName = event.target.value.trim(); lastNameOutput.innerHTML = lastName ? '✔️' : '✖️ Please enter your last name'; }); const emailOutput = document.querySelector('#email-output'); document.querySelector('#email').addEventListener('input', (event) => { email = event.target.value.trim(); emailOutput.innerHTML = /^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.com$/.test(email) ? '✔️' : '✖️ Please enter a valid email address'; }); const hobbyOutput = document.querySelector('#hobby-output'); document.querySelector('#hobby').addEventListener('input', (event) => { hobby = event.target.value.trim(); hobbyOutput.innerHTML = hobby ? '✔️' : '✖️ Please enter your hobby'; }); const form = document.querySelector('#hobby-form'); form.addEventListener('submit', (event) => { event.preventDefault(); if (firstName && lastName && /^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.com$/.test(email) && hobby) { alert(`Hello ${firstName} ${lastName} (${email}), you like ${hobby}!`); firstName = ''; lastName = ''; email = ''; hobby = ''; form.reset(); firstNameOutput.innerHTML = ''; lastNameOutput.innerHTML = ''; emailOutput.innerHTML = ''; hobbyOutput.innerHTML = ''; } else { firstNameOutput.innerHTML = firstName ? '✔️' : '✖️ Please enter your first name'; lastNameOutput.innerHTML = lastName ? '✔️' : '✖️ Please enter your last name'; emailOutput.innerHTML = /^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.com$/.test(email) ? '✔️' : '✖️ Please enter a valid email address'; hobbyOutput.innerHTML = hobby ? '✔️' : '✖️ Please enter your hobby'; } });
💻 Приведенный выше пример кода также можно найти в моем CodePen.
Плюсы ✅:
- Чувство прогресса пользователя. Это мотивирует клиентов, подтверждая, что они на правильном пути, когда они правильно печатают, обеспечивая удовлетворение.
- Шаг за шагом. Пользователи уведомляются о своих ошибках один за другим.
- Более высокие показатели завершения.
Минусы ❌:
- Прервите поток. Мгновенная обратная связь может быть контрпродуктивной, особенно в более длинных формах.
- Раннее обнаружение. Вы только что начали вводить свой адрес электронной почты, и встроенная проверка отображает сообщение об ошибке. Эта ранняя встроенная проверка может свести пользователей с ума.
- Увеличение ошибок. Из-за слишком раннего выполнения или из-за того, что пользователи уже вошли в режим завершения, сообщения об ошибках, как правило, игнорируются. Пользователи продолжают заполнять форму, но отображаемые сообщения влияют на них, отвлекая их, добавляя трения и, наконец, приводя к неправильным данным на следующих шагах.
- Некоторые данные невозможно проверить на стороне клиента, поскольку они зависят от базы данных, что приводит к несоответствиям.
2.4 Дезинфекция пользовательского ввода
Еще одна вещь, которую мы должны иметь в виду, — это предотвращение атак XSS (межсайтовый скриптинг).
Межсайтовый скриптинг (также известный как XSS) — это уязвимость веб-безопасности, которая позволяет злоумышленнику скомпрометировать взаимодействие пользователей с уязвимым приложением. Это позволяет злоумышленнику обойти одну и ту же политику происхождения, которая предназначена для отделения разных веб-сайтов друг от друга. Уязвимости межсайтового скриптинга обычно позволяют злоумышленнику маскироваться под пользователя-жертву, выполнять любые действия, которые может выполнять пользователь, и получать доступ к любым данным пользователя. Если пользователь-жертва имеет привилегированный доступ в приложении, злоумышленник может получить полный контроль над всеми функциями и данными приложения.
Есть несколько полезных библиотек, которые вы можете импортировать в свой проект для проверки и санации вводимых пользователем данных:
📃 Ссылки
📌 https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation
📌 https://designmodo.com/ux-form-validation/
📌 https://portswigger.net/web-security/межсайтовый скриптинг
3. Библиотеки форм
Существует также множество библиотек с открытым исходным кодом, которые вы можете импортировать в свой проект.