От автора: в этой серии статей я буду излагать свои мысли после тщательного изучения современной валидации форм в CSS и JS. Я расскажу про нативные сообщения в HTML-валидации, CSS псевдоклассы :invalid и :valid, а также про Constraint Validation API, которое должно было упростить валидацию форм, но получилось слегка иначе.
В этой статье мы попробуем провести валидацию формы в стиле user-friendly с помощью нативных возможностей HTML, CSS и JS и написания пары небольших скриптов для вытягивания якобы простых строк в Constraint Validation API.
У нас ничего не получится. Мы поймем, что в дизайне были допущены серьезные ошибки, и что не просто так веб-разработчики не используют ни одну из этих техник. Современная валидация форм страдает от следующих проблем:
Между спецификациями HTML, CSS и JS нет четкой связи. Иногда они работают в совершенно разных целях.
Не существует декларативного способа вставки сообщений об ошибках в отдельные поля формы. (есть JS-способ, но он плохо спроектирован)
Сообщения HTML-валидации невозможно стилизовать, а их отвратительное и непостоянное поведение невозможно изменить. Можно только работать с командами show и hide, которые совсем не похожи не отображение и скрытие.
Псевдоклассы CSS – отличная штука, но когда вы начинаете их использовать, все становится не так гладко. Также все способы вставки сообщений об ошибках на псевдоклассах довольно сложные.
Эти проблемы кажутся еще более странными, когда узнаешь, что валидация форм буквально самый старый трюк в JS: когда JS был представлен в Netscape 2, он только и умел делать валидацию форм. У нас было 20 лет, чтобы все исправить, но все осталось как есть.
В своих статьях я обычно расплывчато говорю о совместимости с браузерами, так как вся информация представлена в таблице совместимости. Там можно все подробно посмотреть.
И прежде чем мы начнем еще кое-что: всегда помимо клиентской проверки делайте валидацию не сервере. Если скрипты поломаются, а фолбека под рукой не будет, может случиться что-то очень нехорошее.
И еще кое-что. Огромное спасибо Stéphanie Walter и Geoffrey Crofte за их крайне полезные комментарии к черновику этой статьи.
UI валидации полей формы
Прежде чем окунуться с головой в API и псевдоклассы, мы должны понять, чего хотим добиться. В теории все просто: мы хотим предложить пользователю хороший UX, т.е. понятные и короткие сообщения об ошибках в правильное время в правильном месте.
onsubmit, т.е. когда пользователь пытается отправить форму;
onblur, т.е. когда пользователь переходит на другое поле формы;
onkeypress, т.е. когда пользователь меняет значение поля формы (это может происходить по 10 или 12 раз, прежде чем пользователь перейдет к следующему полю).
Какой сценарий лучше? Christian Holst подробно рассматривает UI и customer experience валидации форм. Его главные советы заключается в том, что сообщения об ошибках необходимо показывать рядом с полями, в которых они возникают. Сообщения необходимо показывать сразу, как только пользователь заполнил поля. Другими словами, лучше всего по событию onblur.
Luke Wroblewski соглашается с такими утверждениями и добавляет замечание, что пользователям лучше постоянно показывать сообщения, а не так, чтобы они со временем пропадали. (Угадайте, как все браузеры показывают сообщения за исключением Firefox на Android?) Это же относится и к сообщениям о положительной проверке.
(Luke также протестировал четвертый вариант: focus + keypress. Сообщения показываются, когда пользователь вводит текст в поле формы. Его тесты убедительно показали, что это плохая идея)
Adrian Roselli также говорит: сообщения об ошибках следует показывать над полем формы, а не снизу, так как на маленьких экранах мобильных устройств сообщения под полем могут закрываться экранной клавиатурой или другими UI-элементами.
Есть и контраргумент – пользователи привыкли к тому, что сообщения об ошибках показываются рядом с полем или под ним. Ширины экрана мобильного устройства может не хватить для показа сообщения рядом с полем, поэтому такое расположение крайне нежелательно. Если разместить сообщение снизу, такой проблемы не возникнет.
Что лучше, решайте сами. Но я замечу, что почти все браузеры размещают сообщения об ошибках под полем формы, хотя некоторые мобильные браузеры нарушают это правило.
С размещением сообщения об ошибке не все так ясно, но появляться оно должно по событию onblur, т.е. когда пользователь дает понять, что он заполнил поле и переходит на следующее. Также должен быть легкий способ добавления своих сообщений об ошибке к отдельным полям формы.
CSS, API и браузеры не выполняют эти простые правила. Невозможно добавить сообщения об ошибках простым декларативным методом. Constraint Validation API основано на событии onsubmit, а CSS :invalid и :valid на событии onkeypress.
Валидация форм на чистом CSS
Валидация форм на чистом CSS работает на псевдоклассах :invalid и :valid. (Есть и другие псевдоклассы типа out-of-range, но мы опустим их, так как их принцип работы точно такой же, и это более частные случаи invalid)
Поиграться с псевдоклассами можете в первом примере на тестовой странице. Валидация перезапускается при каждом нажатии клавиш. Не самый красивый способ и, возможно, запутанный. Пользователю не нужно знать о состоянии введенного значения на каждом этапе: нужно одно четкое сообщение, когда поле будет заполнено целиком. (Пользователь пока не видит сообщения об ошибке. Чуть ниже мы к этому вернемся)
Вне фокуса
К счастью, перейти на onblur довольно легко. Потребуется задействовать немного сложный селектор (спасибо, Krijn, за этот трюк):
1 |
input:invalid:not(:focus) |
Смотрите второй пример на тестовой странице. Теперь невалидные и валидные стили видны только, когда поле вне фокуса, т.е. когда пользователь не вводит в него значение. Уже лучше.
Сообщения об ошибках на чистом CSS
Такие сообщения решают проблему тайминга, но не решают проблему показа самих сообщений. Теоретически решение будет следующим:
1 2 3 |
input:invalid:not(:focus):before { content: attr(data-error-message); } |
Показывайте контент невалидного атрибута data-error-message перед полем, если пользователь не печатает текст. Звучит хорошо, правда?
Увы, увы, но :before и :after нельзя использовать на замененных элементах типа input.
Это нужно исправить. :before и :after не заточены под замененные элементы, однако в Chrome и Safari они работают с диапазонами, чекбоксами и радиокнопками. В Chrome Safari/iOS, но не в Safari/Mac они также работают с полями типа date. (Почему именно эти типы? Я не знаю. Возможно, парочка разработчиков браузеров были пьяны)
Это не работает. Нужно передвинуть сообщение об ошибке за пределы поля. Что-то типа:
1 2 3 4 5 6 7 |
span.errorMessage { display: none; } input:invalid:not(:focus) + span.errorMessage { display: block; } |
Это работает, но сложность объявления сообщений сбивает с толку. Вы можете сказать, что сообщения не относятся к CSS, но, как мы позже увидим, их нет и в HTML и JS. Это фундаментальная проблема спецификация на данный момент.
Проблема с required
Нам может показаться, что мы прошли достойный путь, и валидация на чистом CSS у нас в руках, но все разваливается, когда необходимо учесть обязательные поля формы. Пустые обязательные поля считаются невалидными.
Смотрите псевдоклассы в третьем примере на тестовой странице. Проблема заключается в том, что поле становится невалидным сразу после загрузки страницы. Говорить пользователю, что он ошибся, даже не дав ему возможность взаимодействия с формой – плохой UX.
Можете использовать решение Krijn (добавить текст в плейсхолдер в виде одного пробела!), но тогда придется использовать псевдокласс :placeholder-shown.
1 |
:not(:focus):not(:placeholder-shown):invalid |
Вам еще не смешно? Мы же не просим какой-то скрытый функционал, который нужен только для пары полей. Нам лишь нужно подождать сигнала от пользователя, что он готов, прежде чем проверять поле на валидность.
Другими словами, валидация на чистом CSS вам не подойдет, если у вас есть обязательные поля. А так как почти каждая форма имеет минимум пару обязательных полей, CSS валидация – не вариант.
:user-invalid и :user-error
Эта проблема признана всеми, кто работает с CSS. Решение – сделать в форме псевдоклассы :user-invalid (W3C) или :user-error (WHATWG). Оба значат «поле невалидно после взаимодействия с пользователем». Эти псевдоклассы решили бы большую часть описанных выше проблем.
В настоящее время оба псевдокласса не поддерживаются, но в будущем они будут реализованы и продвинут валидацию в CSS еще дальше. Тем не менее, проблема нативной и простой системы сообщений об ошибках никуда не девается.
:valid и :invalid на других элементах
Мало кто знает, но :invalid и :valid работают не только на input. Эти псевдоклассы можно использовать на fieldset и form. :invalid срабатывает, если fieldset или form содержит минимум одно невалидное поле.
Еще круче, fieldset поддерживает :before и :after. Эта техника может пригодиться:
1 2 3 4 5 6 7 |
fieldset:invalid { border-color: red; } fieldset:invalid:before { content: attr(data-error-message); } |
К сожалению, она не поддерживается в Edge и парочке старых мобильных браузеров.
И если fieldset и form могут быть невалидными, если внутри них есть хотя бы одно невалидное поле, то почему лейблы с невалидными полями не могут быть невалидными? Ну, потому что не могут. Браузеры не поддерживают такой функционал. Мы не можем иметь что-то хорошее, потому что тогда все будет хорошо. Поэтому такой функционал и не поддерживается.
Так завершается часть 1. В следующей статье (скоро) мы поговорим о парочке опций HTML и JS API.
Автор: Peter-Paul Koch
Источник: //medium.com/
Редакция: Команда webformyself.