- PVSM.RU - https://www.pvsm.ru -
Часто меня спрашивают студенты: «Какой элемент сайта самый важный?», на что я им отвечаю — формы. Ведь с помощью форм пользователи совершают почти все конверсионные действия. Именно с этим элементом связано больше всего проблем. В этой статье я постараюсь рассказать, что можно улучшить при взаимодействии с формами. А заодно описать новые возможности работы с ними в браузерах.
Однако, сначала я бы хотел обозначить свою позицию по разработке таких форм. По-моему мнению, правильным подходом при разработке интерфейсов является подход прогрессивного улучшения.
Александр Першин подробно рассказывал об этом подходе в своей статье Progressive Enhancement [1]. Если вы до сих пор не сталкивались с ним, то я вам крайне рекомендую прочитать.
Ещё один нюанс: я не считаю, что интерфейс должен работать и выглядеть одинаково во всех браузерах. Я твёрдо уверен в том, что не стоит мучить пользователей со старыми браузерами вашими многокилобайтными библиотеками — им и без этого плохо.
Так как я буду показывать подходы, использующие новые возможности браузеров, взглянем на текущую статистику рунета. Каждый сможет для себя определить степень готовности пользователей. Я разделил её на две категории: полная и частичная поддержка.
Все приёмы в этой статье оценят 56,8% пользователей. В этой статистике присутствуют браузеры: IE 10, Firefox 11-17, Chrome 4-24, Safari 6, Opera 12.
Часть приёмов оценят 80,1% пользователей. Сюда дополнительно включил поддержку: IE 8-9, Safari 4-5, Opera 10-11.
Мобильные браузеры в статистику не включал, хотя они бы дали ещё больший процент поддержки. Информацию брал из LiveInternet со срезом по России [2]. В расчёт попал средний трафик за 3 месяца (октябрь-декабрь) 2012 года.
Для начала, напомню о новых атрибутах у полей формы, которые буду использовать: required
, autofocus
, placeholder
.
required
— обязательное поле для заполнения;autofocus
— установка фокуса на поле при загрузке страницы;placeholder
— описание поля.
Вместе с этим появилось много новых типов полей: date
, email
, number
, range
и другие. Однако, самое безобидное из них (email
), до сих пор используется с опаской. А ведь для того, чтобы оно заработало специальных действий не нужно. Браузеры, которые не знают этот тип полей, будут считать его текстовым.
Также появились дополнительные селекторы в CSS: E:valid
, E:invalid
, E:required
— с помощью которых можно описывать стилевое оформление полей в разных ситуациях.
Демонстрация работы новых атрибутов полей и их оформления. [3]
Такой подход, конечно, не будет работать в старых браузерах. Однако, так как мы прогрессивно улучшаем форму, нас это не должно сильно беспокоить. Форма остаётся работать даже в старых браузерах. В любом случае, проверка введённых данных должна происходить на серверной стороне. Возьмите себе за правило не доверять пользовательским данным и всегда полностью проверять их на сервере.
Одна из частых проблем заполнения форм — введённые данные теряются. Это может произойти по разным причинам: ошибка сайта, переход по ссылке, в конце концов, может отключиться интернет. Решить эту проблему можно по-разному, например, записывать данные в localStorage
по мере ввода.
- if (window.localStorage) {
- var elements = document.querySelectorAll('[name]');
- for (var i = 0, length = elements.length; i < length; i++) {
- (function(element) {
- var name = element.getAttribute('name');
- element.value = localStorage.getItem(name) || '';
- element.onkeyup = function() {
- localStorage.setItem(name, element.value);
- };
- })(elements[i]);
- }
- }
Раз уж мы используем атрибуты required
, то можно и валидацию сделать по-новому. В спецификации HTML5 [4] для элемента формы добавлен метод checkValidity()
. Он возвращает true
или false
. Стратегия работы формы будет очень простой: если проверка валидации даёт отрицательный результат — мы блокируем отправку формы, в обратном случае — разрешаем отправку.
- submit.disabled = !form.checkValidity();
Теперь добавим возможность отправлять форму без перезагрузки, с помощью аякс. Со второй версией спецификации XMLHttpRequest [5] мы получили много интересного. Например, мы можем больше не заниматься сбором данных для отправки формы, для этого есть объект FormData
.
- form.onsubmit = function(event) {
- if (window.FormData) {
- event.preventDefault();
- var data = new FormData(form);
- var xhr = new XMLHttpRequest();
- var url = form.getAttribute('action') + '?time=' + (new Date()).getTime();
- xhr.open('post', url);
- xhr.onreadystatechange = function() {
- if (xhr.readyState == 4 && xhr.status == 200) {
- // server response: xhr.responseText
- }
- };
- xhr.send(data);
- }
- };
При работе с асинхронными запросами следует помнить, что некоторые браузеры кэшируют результат. Например, это делает Internet Explorer, Mobile Safari (iOS 6) и другие. Чтобы избежать эту проблему, можно добавлять к адресу запроса текущее время.
Сейчас ответ от сервера мы получаем в текстовом виде (xhr.responseText
), но со временем это изменится. Например, если мы точно знаем, что ответом будет JSON, мы можем получить JavaScript объект сразу.
- var xhr = new XMLHttpRequest();
- xhr.open(method, url);
- xhr.responseType = 'json';
- xhr.onreadystatechange = function() {
- if (xhr.readyState == 4 && xhr.status == 200) {
- // server response: xhr.response
- }
- };
- xhr.send();
Обратите внимание, что ответ сервера будет в свойстве xhr.response
. А свойство xhr.responseType
может принимать и другие значения, например: arraybuffer
, blob
, document
.
Демонстрация сохранения данных формы и отправки их с помощью XMLHttpRequest. [6]
После успешной отправки формы я советую оставить контактную информацию в localStorage
, а остальное очистить. Таким образом, если пользователь захочет ещё раз отправить форму, часть информации уже будет заполнена.
Плавно переходим на работу с файлами в формах. До недавнего времени почти никаких средств для работы с файлами не было. Но всё меняется. Начнём с простого — новые атрибуты для полей загрузки файлов.
multiple
— позволяет выбирать несколько файлов;accept
— даёт возможность указывать, какие файлы выбирать.Допустим, мы хотим к нашей форме добавить возможность закачивать несколько фотографий. Такое поле будет выглядеть как-то так.
- <input type="file" name="image" accept="image/*" multiple>
Хочу напомнить: поле с такими атрибутами будет работать в старых браузерах. Ограничением будет:
Попробуем улучшить опыт взаимодействия с файлами. Раз мы ожидаем от пользователей добавления фотографий, логично сделать возможным предварительный просмотр. Для этого мы будем использовать объект FileReader
из спецификации File API [7].
- document.querySelector('[type=file]').addEventListener('change', function() {
- [].forEach.call(this.files, function(file) {
- if (file.type.match(/image.*/)) {
- var reader = new FileReader();
- reader.onload = function(event) {
- var img = document.createElement('img');
- img.src = event.target.result;
- div.appendChild(img);
- queue.push({file: file, element: img});
- };
- reader.readAsDataURL(file);
- }
- });
- }, false);
Таким образом, все выбранные фотографии мы сразу же отображаем на сайте.
А для того, чтобы отправить их с помощью аякса, мы собираем их в массиве queue
. Ранее в статье мы использовали объект FormData
, сейчас мы просто добавим к нему список файлов.
- var data = new FormData(form);
- queue.forEach(function(element) {
- data.append('image', element.file);
- });
Только и всего, остальное остаётся таким же. Форма будет отправлена с файлами без перезагрузки.
Демонстрация предварительного просмотра фотографий и отправки их с помощью аякс. [8]
Попробуем уделить больше внимания файлам. Добавим возможность перетаскивать файлы с компьютера сразу в форму. При этом логика предварительного просмотра и отправки без перезагрузки должна остаться. Для начала давайте выделим работу с предварительным просмотром в отдельную функцию.
- function previewImages(files) {
- [].forEach.call(files, function(file) {
- if (file.type.match(/image.*/)) {
- var reader = new FileReader();
- reader.onload = function(event) {
- var img = document.createElement('img');
- img.src = event.target.result;
- div.appendChild(img);
- queue.push({file: file, element: img});
- };
- reader.readAsDataURL(file);
- }
- });
- }
Допустим, зоной для перемещения файлов будет блок с классом wrapper
. Добавим события для него.
- var file = document.querySelector('[type=file]');
- var dropzone = document.querySelector('.wrapper');
- file.addEventListener('change', function() {
- previewImages(this.files);
- this.value = '';
- }, false);
- dropzone.addEventListener('dragover', function(event) {
- event.preventDefault();
- dropzone.classList.add('active');
- event.dataTransfer.dropEffect = 'copy';
- }, false);
- dropzone.addEventListener('drop', function(event) {
- event.preventDefault();
- dropzone.classList.remove('active');
- previewImages(event.dataTransfer.files);
- }, false);
Как видите, мы добавили события начала (dragover
) и конца (drop
) перемещения файлов. Все перемещённые файлы мы передаём функции previewImages
. Таким образом, наша форма работает одинаково с файлами выбранными через сайт и перемещёнными с компьютера.
Фотографии бывают очень большими, поэтому попробуем отобразить процесс загрузки. Для визуализации этого процесса я возьму элемент progress
, а вы можете взять div
с двигающимся фоном. Сам процесс будет происходить в событии progress
из спецификации XMLHttpRequest.
- var xhr = new XMLHttpRequest();
- xhr.upload.addEventListener('progress', function(event) {
- if (event.lengthComputable) {
- progress.value = Math.round((event.loaded * 100) / event.total);
- }
- }, false);
Демонстрация drag & drop и прогресса загрузки файлов. [9]
Наша простая форма имеет ряд значительных улучшений в области UX.
При этом, так как мы действовали в соответствии с прогрессивным улучшением, наша форма работает везде:
Работа FileReader
на iOS 6.
Уверен, занимаясь улучшением UX форм, можно найти более интересные решения. Пожалуйста, добавьте ваши решения, советы, критику в комментариях ниже. Спасибо!
Автор: meritt
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/23385
Ссылки в тексте:
[1] Progressive Enhancement: http://habrahabr.ru/post/157115/
[2] LiveInternet со срезом по России: http://www.liveinternet.ru/stat/ru/browsers.html?slice=ru;period=month
[3] Демонстрация работы новых атрибутов полей и их оформления.: http://simonenko.su/dev/improve-web-form/attributes.html
[4] HTML5: http://www.w3.org/TR/html5/
[5] XMLHttpRequest: http://www.w3.org/TR/XMLHttpRequest/
[6] Демонстрация сохранения данных формы и отправки их с помощью XMLHttpRequest.: http://simonenko.su/dev/improve-web-form/localstorage.html
[7] File API: http://www.w3.org/TR/FileAPI/
[8] Демонстрация предварительного просмотра фотографий и отправки их с помощью аякс.: http://simonenko.su/dev/improve-web-form/ajax-upload.html
[9] Демонстрация drag & drop и прогресса загрузки файлов.: http://simonenko.su/dev/improve-web-form/drag-and-drop.html
[10] Источник: http://habrahabr.ru/post/163739/
Нажмите здесь для печати.