• От простого к сложному

    Насколько сложной может быть форма из двух полей, отправляющая данные аяксом на сервер? С современными фреймворками вроде jQuery, код может выглядеть примерно так:

    $.post('my-url.php', $('form#my-form').serialize());
    

    Но это минимальный код; код из серии «создаём свой блог/цмс за 5 минут», которым так любят заманивать создатели всяких движков и фреймворков. А что нужно сделать, чтобы такая форма стала удобной и понятной для конечного пользователя, то есть «человеческой»?

    Недавно мы запустили новый скидочный проект BigBuzzy. Так как на рынке уже существует масса подобных проектов, единственный способ выделиться из толпы — это сделать проект очень удобным для пользователя, как с концептуальной точки зрения (не надо ждать, пока наберётся определённая группа; удобные способы оплаты), так и с технологической.

    Одна из фич проекта — отправка купленного купона себе на телефон по СМС. Так как на месте проведения акции нужно предъявить только номер купона, нет особого смысла печатать его на бумаге, можно показать и с телефона. Увидеть форму в действии можно на бигбаззи, купив 10 купонов на этой странице. В этой форме как раз два поля: код и номер телефона. Посмотрим, что можно в ней улучшить.

    Алгоритм работы

    Прежде, чем приступать к работе, нам нужно определиться, какие функции несёт в себе эта форма. Когда пользователь оплатил купон, ему предоставляется на выбор два действия: распечатать или отправить по СМС. Чтобы отправить по СМС, нужно знать номер телефона пользователя. В принципе, можно заставить его ввести этот номер при регистрации, однако это с большой долей вероятности отпугнёт потенциальных покупателей: с чего это вдруг сайт требует с меня номер телефона? Поэтому форму будем показывать только тогда, когда пользователь нажмёт на кнопку «Отправить по СМС»: в этом случае ему будет понятно, почему требуется ввести номер телефона.

    А что делать, если пользователь уже ввёл номер при отправке первого купона и хочет отправить второй? Логично, что он захочет отправить купон на тот же самый номер. Нужно ли в этом случае показывать форму? Решаем, что нужно. Во-первых, пользователь может захотеть отправить купон своему другу в качестве подарка или этот друг только что позвонил и попросил выручить, докупив ещё два купона на боулинг. Заставлять пользователя идти в Личный кабинет и менять там номер телефона как-то уж совсем неправильно. Во-вторых, появляющаяся форма будет своеобразным подтверждением действия (вдруг он случайно нажал на эту кнопку?). Поэтому на нажатие конпки будем запускать анимацию появления формы, а повторное нажатие на эту же кнопку отправить данные на сервер:

    $(document.body).delegate('.coupon-button', 'click', function(/* Event */ evt) {
    	var elem = $(this);
    	if (elem.hasClass('coupon-button-sms')) {
    		var coupon = elem.closest('.coupon');
    		if (!coupon.length)
    			return;
    
    		if (!coupon.hasClass('coupon-sms-mode')) {
    			// переключаем купон в режим отправки СМС
    			switchSMSMode(coupon);
    		} else  {
    			// пытаемся отправить данные на сервер
    			tryToSendSMS(coupon);
    		}
    	}
    });
    

    Функцию анимации появления формы с вводом номера телефона я тут не привожу, так как она довольно большая и скучная (желающие могут посмотреть исходник). Первая проблема, которая может возникнуть на этом шаге: пользователь может случайно дважды кликнуть на кнопку, что приведёт к моментальной отправке СМС, если там уже был вбит номер. Поэтому нужно поставить блокировку на выполнение действия. Это стандартный подход в анимированных интерфейсах, который позволяет избежать случайных нажатий или даже поломки всего интерфейса (может наложиться несколько анимаций друг на друга). Введём переменную is_animating, которая будет равна true в момент анимации. Обнулять эту переменную будем после завершения анимации через callback-функцию для switchSMSMode():

    $(document.body).delegate('.coupon-button', 'click', function(/* Event */ evt) {
    	var elem = $(this),
    		is_animating = false;
    
    	if (elem.hasClass('coupon-button-sms')) {
    		var coupon = elem.closest('.coupon');
    		if (!coupon.length || is_animating)
    			return;
    
    		if (!coupon.hasClass('coupon-sms-mode')) {
    			// переключаем купон в режим отправки СМС
    			is_animating = true;
    			switchSMSMode(coupon, function(){
    				is_animating = false;
    			});
    		} else  {
    			// пытаемся отправить данные на сервер
    			tryToSendSMS(coupon);
    		}
    	}
    });
    

    Ввод данных

    Форму мы показали, теперь нужно упростить пользователю ввод данных. Тут надо помнить, что существует множество моделей поведения пользователя. Кто-то быстро введёт код и переключится табом на следующее поле, кто-то будет минуту набирать три цифры и схватиться за мышку, чтобы переключиться на следующее поле, кто-то будет ожидать, что введённая последовательность из 10 цифр автоматически распределиться между полями. Нужно постараться удовлетворить максимальное количество пользователей. Поэтому будем следить за полем «код», и когда там будет 3 цифры — автоматически перебросим фокус на поле с номером:

    $('.phone-code').keyup(function(){
    	if (this.value.length == 3)
    		$(this).next('.phone-num').focus();
    });
    

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

    $('.phone-code').bind('keyup change', function(/* Event */ evt) {
    	var field = $(evt.target),
    		last_length = field.data('last_length') || 0,
    		cur_length = field.val().length,
    		max_length = parseInt(field.attr('maxlength')) || 3,
    		selection = getSelectionRange(field[0]);
    
    	if (cur_length > last_length && cur_length == max_length && selection && selection.start == cur_length) {
    		field.next('.phone-number').focus();
    	}
    
    	field.data('last_length', cur_length);
    });
    
    function getSelectionRange(elem) {
    	if ('selectionStart' in elem) { // W3C's DOM
    		return {
    			start: elem.selectionStart,
    			end: elem.selectionEnd
    		};
    	} else if (document.selection) { // IE
    		elem.focus();
    
    		var range = document.selection.createRange(),
    			content = elem.value;
    
    		if (range === null) {
    			return {
    				start: 0,
    				end: content.length
    			};
    		}
    
    		var re = elem.createTextRange();
    		var rc = re.duplicate();
    		re.moveToBookmark(range.getBookmark());
    		rc.setEndPoint('EndToStart', re);
    
    		return {
    			start: rc.text.length,
    			end: rc.text.length + range.text.length
    		};
    	} else {
    		return null;
    	}
    }
    

    Но тут возникает ещё одна проблема. Далеко не каждый пользователь догадается, что при заполнении поля с кодом фокус перекинется на следующее поле. Он может не глядя на монитор набрать 3 цифры, нажать на таб и набрать ещё 7 цифр. В этом случае наша «помощь» только помешает пользователю, потому что фокус будет где-то совершенно в другом месте. Чтобы такого не случилось, нужно временно заблокировать нажатие на таб на поле с номером телефона после того, как фокус был автоматически переброшен:

    $('.phone-code').bind('keyup change', function(/* Event */ evt) {
    	var field = $(evt.target),
    		last_length = field.data('last_length') || 0,
    		cur_length = field.val().length,
    		max_length = parseInt(field.attr('maxlength')) || 3,
    		selection = getSelectionRange(field[0]);
    
    	if (cur_length > last_length && cur_length == max_length && selection && selection.start == cur_length) {
    		var num_field = field.next('.phone-number');
    
    		// временно блокируем Tab на следующем поле
    		num_field.data('tab_locked', true);
    		setTimeout(function() {
    			num_field.data('tab_locked', false);
    		}, 1000);
    
    		num_field.focus();
    	}
    
    	field.data('last_length', cur_length);
    });
    
    $('.phone-number').bind('keydown keyup keypress', function(/* Event */ evt) {
    	var field = $(evt.target);
    	if (field.data('tab_locked') === true && evt.keyCode == 9) {
    		// блокируем работу клавиши Tab
    		evt.preventDefault();
    	}
    });
    

    Валидация данных

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

    Вводить жёсткий формат записи числовых данных вроде номера телефона или кредитной карты — первый признак полового бессилия разработчика. Для номера телефона нам нужно 10 цифр (код + сам номер), и не важно, написаны цифры слитно или разделены пробелами или дефисами. Пользователь должен написать номер в привычном ему формате, чтобы легче было проверять правильность. Поэтому при валидации удалим из полей все не числовые символы и проверим длину того, что осталось:

    function validatePhoneNumber(coupon) {
    	coupon = $(coupon);
    
    	var re_num = /[^0-9]/g,
    		phone_code = coupon.find('.phone-code'),
    		phone_num = coupon.find('.phone-number'),
    		is_valid_code = phone_code.val().replace(re_num, '').length == 3,
    		is_valid_num = phone_num.val().replace(re_num, '').length == 7;
    
    	if (!is_valid_code)
    		blinkField(phone_code);
    	if (!is_valid_num)
    		blinkField(phone_num);
    
    	return is_valid_code && is_valid_num;
    }
    

    Если введённые данные не верны, то мы должны сообщить об этом пользователю. Пространство у нас очень ограничено, поэтому будем «моргать» полем, указывая, что там что-то не так. Функция добавляет и удаляет определённый класс у элемента через заданный промежуток времени, в CSS укажем этому классу красный цвет:

    function blinkField(fld) {
    	fld = $(fld);
    	if (fld.data('blink_timer'))
    		clearInterval(fld.data('blink_timer'));
    
    	var blink_count = 6;
    
    	fld.data('blink_timer', setInterval(function() {
    		fld.toggleClass('coupon-field-warning');
    		if (blink_count-- < 0) {
    			fld.removeClass('coupon-field-warning');
    			clearInterval(fld.data('blink_timer'));
    		}
    	}, 100));
    }
    

    Отправка данных

    Данные от пользователя получили и проверили, настало время отправить их на сервер. Опять же, тут есть две потенциальные проблемы. Первая — это слишком медленный интернет. Нужно показать пользователю, что процесс запущен и скоро что-то произойдёт. Вторая проблема не так очевидна — это слишком быстрый интернет. Настолько быстрый, что пользователь может и не понять, а произошло ли что-то, отправились ли данные или возникла ошибка? Чтобы избавиться от этих проблем, нужно показать показать некий индикатор процесса, причём этот индикатор должен искусственно замедлить получение фидбэка от сервера. Он должен отображаться на странице некоторое время, чтобы пользователь успел понять, что запущен некий процесс.

    Кто-то решает эту задачу так: показывает индикатор, ждёт определённое время и уже после этого отправляет запрос на сервер, а после получения ответа прячет индикатор. Так делать не правильно: в случае с медленным интернетом таймаут слишком сильно увеличивается; сам запрос мог бы запросто отработать за то время, пока показывается индикатор. Поэтому мы устроим небольшую гонку: что последним закончится выполняться — таймер индикатора или запрос на сервер — то и покажет фидбэк пользователю:

    function sendSMSRequest(coupon) {
    	coupon = $(coupon);
    	var phone_code = coupon.find('.phone-code').val(),
    		phone_num = coupon.find('.phone-number').val();
    
    	coupon.addClass('coupon-preloader');
    	// запускаем индикатор
    	preloader.start();
    
    	// два условия отображения фидбэка: закончился таймер и пришёл ответ от сервера
    	var timer_reached = false,
    		response_received = false;
    
    	function checkStatus() {
    		if (timer_reached && response_received) {
    			// останавливаем индикатор
    			preloader.start();
    			coupon.removeClass('coupon-preloader');
    
    			// прячем форму с номером телефона
    			switchSMSMode(coupon);
    		}
    	}
    
    	// устраиваем гонку между таймером и запросом
    	setTimeout(function() {
    		timer_reached = true;
    		checkStatus();
    	}, 1000);
    
    	$.get(sms_url, {
    		id: coupon.attr('id'),
    		action: 'send_sms',
    		phone: phone_code + phone_num
    	}, function(data) {
    		response_received = true;
    		checkStatus();
    	}, 'json');
    }
    

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

    code-example

  • 89 комментариев

    1. Игорь Кононученко
      6 июля 2010

      «Стало» является отличной заготовкой для рефакторинга по мотивам книги Фаулера.

    2. 6 июля 2010

      Спасибо. Работа с фокусом и валидацией — прекрасна 🙂

    3. 6 июля 2010

      ещё одно доказательство, что «простой код из двух строк» — ужас лохотронский, но очень любимый фрилансерами. «работает же» 🙂

    4. kizu
      6 июля 2010

      Хороший пример.

      Два маленьких предложения:
      — при нажатии на «послать по СМС» ставить фокус на первое поле;
      — хочется чтобы контролы «код» и «номер телефона» были ещё более «объединены» — чтобы можно было перемещаться из начала второго в конец первого при помощи клавиатуры (влево-вправо), и если я нахожусь во втором и хочу ввести новый телефон с самого начала, и буду удалять всё бекспейсом — хочется, удалив всё во втором поле, следующим бекспейсом удалять то, что находится в первом. Хотя тут спорно — перестанет «правильно» работать отмена, ну и вообще подобное поведение — тема для отдельного контрола с отдельной статьёй, наверное 🙂

    5. 6 июля 2010

      Ещё одна деталь — после клика по кнопке «послать по смс» приходится ещё кликать по полю кода, фокус автоматически туда не устанавливается. В Хроме, по крайней мере.

    6. Сергей Чикуенок
      6 июля 2010

      Если сразу ставить фокус на поле «код», то пропадёт плэйсхолдер и пользователь может не понять, что от него требуется

    7. 6 июля 2010

      Может его убирать при первом нажатии? ИМХО, вот тут как раз лишний клик, его и стоит убрать.

      Кстати, для особо непонятливых вокруг поля «код» можно поставить скобочки.

    8. Сергей Чикуенок
      6 июля 2010

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

    9. kizu
      6 июля 2010

      Да, есть такое, действительно. Хотя мне сложно представить, что кто-то может не догадаться о том, что в данном примере надо ввести именно три цифры кода 🙂

      Кстати, сейчас вспомнил — в speed dial оперы (кажется, с 10.5) они решили проблему тем, что плейсхолдер не исчезает пока ты что-либо не напишешь.

      Жаль только, что дефолтный вебкитовский плейсхолдер не получится так подхачить. Почему-то в спецификациях CSS3 столько всего уже понаписали, а селектора, по которому можно было бы отличить пустой инпут и заполненный — до сих пор нет.

    10. Сергей Чикуенок
      6 июля 2010

      Там везде используются плэйсхолдеры в виде <label>-элемента, дефолтные не используется в том числе потому, что нельзя ими нормально управлять.

      А насчёт догадок пользователей: опыт разгребания суппорта показывает, что люди даже о более простых вещах не могут догадаться 🙂

    11. kizu
      6 июля 2010

      Ну да, лучше перебдеть, чем недобдеть )

      Лейбл я увидел, просто отсутствие подобного селектора — всё, что мешает написать свой плейсхолдер только на CSS.

    12. Сергей Чикуенок
      6 июля 2010

      Кстати, для этого проекта я специально разработал анимированный JS-прелоудер, который можно тонко настраивать: начальный цвет, конечный цвет, честная полупрозрачность, количество кружков, их размер, радиус прелоудера, скорость движения. Весит примерно 3 КБ минимизирванным (1,5 КБ в gzip), в современных браузерах работает с субпиксельным сглаживанием. Планирую ещё пару штук сделать и выложить отдельным проектом.

    13. vladfrandom
      6 июля 2010

      if (!coupon.hasClass(‘coupon-sms-mode’)) {
      // переключаем купон в режим отправки СМС
      switchSMSMode(coupon);
      } else {
      // пытаемся отправить данные на сервер
      tryToSendSMS(coupon);
      }

      Всё никак не пойму, это феншуй такой: проверку через отрицание писать?
      Как бы понятно, что так логика порядка действий описана, но всё-таки?

    14. 6 июля 2010

      Я для определения анимированности элемента использую селектор :animated

      $(«#id»).animate(…);

      и в любом другом месте кода

      if ($(«#id»).is(«:animated»)) { … }

      Крайне удобно.

    15. 6 июля 2010

      а тут приходит злой дядя с выключеным ДжСом 🙂

    16. 6 июля 2010

      Но он не наш клиент

    17. yetanotherape
      6 июля 2010

      Все это конечно круто, но зачем выделять «код» в отдельное поле?

    18. yetanotherape
      6 июля 2010

      Кстати если не ввести «код» (я же в дефолт-сити, чувак!), но ввести семизначный номер телефона, ни один валидатор не подаст виду, что что-то не так.

    19. Сергей Чикуенок
      6 июля 2010

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

    20. Олег Подчашинский
      6 июля 2010

      Индикатор останавливается тоже через preloader.start()? 🙂
      Это в функции checkStatus

    21. 6 июля 2010

      От простого, к очень сложному.
      И HTML у вас там страшноватый. Бррр.

    22. Сергей Чикуенок
      6 июля 2010

      А что страшного в HTML? Пока ни один пользователь на него не жаловался.

    23. 6 июля 2010

      http://bigbuzzy.ru/catalog/biscotti/

      Сергей, у флешки wmode=’opaque’ кто-то забыл (перекрывает слой с заказом)

    24. Сергей Чикуенок
      6 июля 2010

      wmode=’opaque’ начинает сильно грузить процессор. По идее, в IE флэшки должны скрываться а в других браузерах проблема с z-index и флэшом должна быть решена. В каком браузере смотрите?

    25. 6 июля 2010

      Ещё бы пользователи жаловались ))
      Это я так, техноэстетствую. Не люблю кучу вложенных дивов и всё такое.

    26. Анатолий Rr Буров
      6 июля 2010

      В Хроме 5.0.375.70 (Win XP) на странице, указанной Эльдаром, флешка действительно перекрывает «окошко» с заказом.

    27. 6 июля 2010

      Только не понятно, зачем пользователю давать вводить буквы? Может быть, сразу фильтровать всё, кроме цифр?

    28. Flack
      6 июля 2010

      Очень крутая статья.
      Спасибо.

    29. Kalan
      6 июля 2010

      А если телефон начинается не с +7, что тогда пользователю делать?

    30. Nail
      6 июля 2010

      Вот мне что в итоге остается непонятным (может я из провинции, а в столице это ни для кого не вопрос):
      зачем телефон делить на две части код-номер? Собственно, из-за чего и всякие пляски с бубном.
      Я вот например знаю свой номер телефона таким: 8-902-83-4…. — всего 10 цифр, какие из них «код»? какие «номер»?
      Опять же, сейчас в форме не предусмотрен код страны, соответственно гости с Украины идет лесом.

      Зачем мучать котенка, почему бы не сделать единое поле с подсказкой?

    31. 6 июля 2010

      Сергей, это баг — http://i.satsura.com/s/20100706-74o-319kb.jpg или задумка?

    32. Сергей Чикуенок
      6 июля 2010

      Kalan, пока сайт предлагает услуги для жителей Москвы, поэтому международный код пока особо не нужен. В дальнейшем, когда появятся другие города и страны, будем думать.

      Nail, я в целом согласен, что второе поле может быть избыточным в данном случае, но так придумал дизайнер и у меня нет оснований полагать, что он это сделал просто так. Я работал с тем, что мне дали.

      Валерий Сацура, вообще, конечно, не баг, но выглядит стрёмно, так как мы очень торопились. Потом переделаем.

    33. 6 июля 2010

      BigBuzzy как-то подтормаживает. И дизайн мне не понравился. А юзабилити — да, прикольное.
      Это я к тому, что в гармонии все должно быть: если хотите выделиться, то все на высоте, а не отдельные части.

    34. Сергей Чикуенок
      7 июля 2010

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

    35. Пользователь
      7 июля 2010

      Оплату через web-money/я.деньги когда прикрутят?
      анимация — это хорошо, но сайт на половину выполняет действительно нужную мне функцию.

    36. yetanotherape
      7 июля 2010

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

    37. 7 июля 2010

      >>> wmode=’opaque’ начинает сильно грузить процессор. В каком браузере смотрите?

      FF 3.6.6 winXP

    38. Виктор
      7 июля 2010

      Форма получилась прекрасная! Единственный вопрос, как обеспечить её корректную работоспособность в течение длительной жизни проекта. Т.е. как обеспечить покрытие автоматизированными тестами или просто тесткейсами.

    39. Миша
      7 июля 2010

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

    40. Максим
      7 июля 2010

      Сергей, а будет ли на сайте система оповещения об окончания срока действия купонов?

      Насколько я знаю, в подобных проектах такого нет (на darberry, например, очень не хватает). То есть, купил купоны, забыл, а за пару недель или за неделю приходит письмо с напоминанием, что надо бы уже воспользоваться, а то все зря.

    41. Алексей
      7 июля 2010

      > Поэтому будем следить за полем «код», и когда там будет 3 цифры — автоматически перебросим фокус на поле с [телефоном]

    42. Сергей Чикуенок
      7 июля 2010

      Оплату через web-money/я.деньги когда прикрутят?

      Уже в процессе. Обычно этим платёжным системам нужно сначала показать готовый сайт, потом они 2—4 недели будут нас проверять, и только потом разрешат принимать деньги.

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

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

      Единственный вопрос, как обеспечить её корректную работоспособность в течение длительной жизни проекта. Т.е. как обеспечить покрытие автоматизированными тестами или просто тесткейсами.

      Честно говоря, я никогда об этом не задумывался именно по причине огромного количества нюансов, которые нужно проверять. На бигбаззи я постарался минимизировать возможные проблемы: сама форма оплаты сделана в виде ядра и независимых модулей, которые никак друг с другом не связаны. Удаление любого модуля никак не скажется на работоспособности формы. Так что при поддержке проверять, в основном, нужно только один модуль (вручную), а не всю форму целиком.

      Сергей, а будет ли на сайте система оповещения об окончания срока действия купонов?

      Спасибо за идею. Завёл тикет, думаю, сделаем.

    43. 7 июля 2010

      Win 7 x64 FF 3.6.6

      При первом входе в профиль пользователя и попытке заполнить код телефона не даёт ввести последнюю цифру, вводятся только 2.

    44. Денис
      7 июля 2010

      Моё мнение: делали-делали-делали, а всё равно получилась какашка. В итоге перегруженная js’ом форма и никакого удобства.

      Не проще было сделать одним input’ом? Тогда мне кажется было бы более оправдано такое количество кода(даже меньше кода получилось)

      «Всё гениальное — просто» — глядя на такие вещи, ненароком осознаёшь что это сказал очень умный человек.

      UDP: Не осилил все комментарии(там может и предлагали один input) т.к. затошнило от первых отзывов где это безобразие расхваливают.
      UDP2: Ресурс позволяет оставлять комментарии без всяких регистраций(спс владельцам). И это заслуживает большего внимание чем то, что сделали вы.

    45. Alexey
      7 июля 2010

      Еще голос в пользу одного инпута под номер.
      Помимо названных моментов с неудобностью редактирования добавлю свои 2 коп:
      * За пределами дефолт-сити таки есть города с шестизначной нумерацией. Не поверите, но жителям этих городов гораздо удобнее дробить номер на 4+6 цифр, а не на 3+7. И ваше гестапо с 3-символьным полем для кода просто убивает.
      * Друг прислал мне номер телефона в аську. Я его копирую и пытаюсь вставить в вашу псевдо-удобную форму. Что получаю в итоге? Правильно — большую жирную жопу =)

      Так дайте юзерам возможность ввести 10 цифр своего номера с любым количеством разделителей в любой удобной ему форме, будь то:
      * (123) 123-45-67
      * 1234-12-1234
      * (123)12/345/67
      * 1?2?3??1?2?3?4?5?6?7
      * PROFIT!
      Все это безобразие легко превращается в 1231234567 одной строчкой кода.

      Кстати абсолютно все смс-гейты принимают номер в международном формате +71231234567 или +7.1231234567 Незачем выдумывать искусственные ограничения там, где они не нужны.

    46. 7 июля 2010

      Alexey, +1! Не надо навязывать пользователю формат, пусть вводит как хочет, как удобней.
      Я вот даже для денег такое сделал, что уж про номера телефона говорить. Больше свободы. И проще, кстати, а то раздули JS дальше некуда уже, а всё равно удобства нет.

    47. Сергей Чикуенок
      7 июля 2010

      За пределами дефолт-сити таки есть города с шестизначной нумерацией.

      Сайт сейчас работает только на Москву. И, как я уже написал, если будут жалобы, то будем исправлять.

      Друг прислал мне номер телефона в аську. Я его копирую и пытаюсь вставить в вашу псевдо-удобную форму. Что получаю в итоге? Правильно — большую жирную жопу =)

      Это постараюсь сделать.

      Проблема одного инпута в том, что он вызывает дополнительные вопросы. Например, нужно ли писать +7 или 8 в начале, нужно ли как-то отдельно оформлять код. В том виде, в котором сейчас есть форма, с первого взгляда понятно, что требуется от пользователя.

    48. 7 июля 2010

      Проблема одного инпута в том, что он вызывает дополнительные вопросы. Например, нужно ли писать +7 или 8 в начале, нужно ли как-то отдельно оформлять код.

      Сделать сереньким подсказку просто и всё. Но всё равно обрабатывать номера в любом формате.

    49. Сергей Чикуенок
      7 июля 2010

      Как именно должна выглядеть подсказка и куда её воткнуть? И почему нужно объяснять, как вводить номер телефона?

    50. 7 июля 2010

      Однако, надо признать, что здесь все комментарии от разработчиков и немного гиков. У постоянной аудитории обсуждаемого сайта могут быть совсем другие мысли, и это действительно проблема. Иногда сложно посмотреть на сайт глазами простого пользователя.

    51. Сергей Чикуенок
      7 июля 2010

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

    52. 7 июля 2010

      Как именно должна выглядеть подсказка и куда её воткнуть? И почему нужно объяснять, как вводить номер телефона?

      Например, сереньким текстом под полем ввода.
      Прямо так: «Например, +7(902)930-xx-xx». То же самое можно запихнуть просто в атрибут title.

      И почему нужно объяснять, как вводить номер телефона?

      А насчёт догадок пользователей: опыт разгребания суппорта показывает, что люди даже о более простых вещах не могут догадаться…

    53. Alexey
      7 июля 2010

      В технологическом плане ничего не стоит одинаково обрабатывать номера вида +7ХХХХХХХХХХ, 8ХХХХХХХХХХ и просто ХХХХХХХХХХ. Но есть категория людей, которые не вобьют номер в привычной им форме, а тупо зависнут без подсказки/ограничения — с такими я затрудняюсь что делать.

    54. 7 июля 2010

      И ещё дополнение.
      После нажатия «получить SMS» стоит сразу ставить курсор в поле ввода номера.
      Вот это действительно удобно.

    55. Сергей Чикуенок
      7 июля 2010

      После нажатия “получить SMS” стоит сразу ставить курсор в поле ввода номера.

      С этим я полностью согласен, но пока есть некоторые проблемы

    56. Alexey
      7 июля 2010

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

    57. 7 июля 2010

      А QR-коды у нас вообще популярны разве? Я так понял, они в буржунете распространены (в Японии, особенно), а наши и не поймут. Кстати, вот у меня, например, телефон вообще без прибамбасов 🙂

    58. Сергей Чикуенок
      7 июля 2010

      Если честно, то в данном контексте в QR-кодах нет особого смысла. Вы дольше будете целиться в монитор, чем вводит номер своего телефона для получения кода по СМС

    59. Alexey
      7 июля 2010

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

    60. 7 июля 2010

      Здравствуйте.
      Там у вас Flash залезает на форуму авторизации. Во всяком случае в хроме.

    61. 10 июля 2010

      if the whole world were coders then we’d never have to turn 1 line into 1000 🙂

      I submit a project to a client and it’s less than 100k of php source, by the time they have ‘user tested’ it, I have had to make it over 1000k in validation and ‘anti stupid user’ code!

      great ideas here, I learned a lot (even with google translation)

    62. Eugene
      11 июля 2010

      Сори за оффтоп

      На сайте http://bigbuzzy.ru/ если пойти и попытаться платить PayPal, то логотип выводить неправильно — белые буквы на белом фоне не видны, а видны только желтые значки

    63. 12 июля 2010

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

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

      P. S. Нет ничего хуже подсказок, навязывающих неправильный формат.

    64. Михалыч
      18 июля 2010

      В примере (http://media.chikuyonok.ru/coupon/), думаю можно убрать подсветку с кнопки и курсор (поинтер) при открытии формы для ввода номера телефона. Ноу?

    65. fekss
      6 августа 2010

      Сергей, можете показать как все это работает внутри, пару скринов админки хотя бы, очень любопытно)

    66. 15 сентября 2010

      Да, точно. А что будет с теми кто не правильно введет номер телефона?

    67. 28 января 2011

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

      У меня есть вопрос по поводу формы — http://media.chikuyonok.ru/coupon/
      На мой взгляд в ней есть очень серьезная юзабилити недоработка — сказал о ней тупым менеджерам бигбази, они только развели руками.
      Я сам пользователь этого сайта, когда я покупаю купоны то они все оказываются в одном списке — http://media.chikuyonok.ru/coupon/ и при этом совершенно не понятно какие купоны на одно мероприятие, какие на разное? Допустим я купил 10 купонов на 5 мероприятий, причем количество купонов по каждому предложению разное.
      Как находясь на этой странице понять какой купон из этих именно на боулинг? И как распечатать еще 3 купона на боулинг для моих друзей? Почему нельзя распечать купоны на боулинг на одном листе? А если у меня купоны на компанию в пейтбол — 15 человек?
      Но само важное — этот список не отображает срок действия купона, обратную ссылку на описание предложения(распечатал купон и сидишь и думаешь, а как узнать адрес?(я лично просматривал архив бигбази — бред)) и название предложения.
      В этом списке отсутствуют совершенно групповые и менеджерские операции.

    68. Сергей Чикуенок
      28 января 2011

      Шум, я не понимаю, зачем вы ссылаетесь на мою тестовую страницу, при том что большинство описанных вами проблем давно решены на бигбаззи?

    69. Роман
      15 мая 2011

      Как бесят два поля вместо одного для номера телефона в куче мест. И вы туда же…

    70. Сергей Чикуенок
      15 мая 2011

      В чём проблема двух полей в номере телефона?

    71. Роман
      15 мая 2011

      Смысл растить энтропию и создавать сложности пользователю, которые самим же потом и решать?
      Давайте для e-mail три поля делать.

      Часть до собачки, часть после собачки окромя TLD, а потом TLD в отдельном поле.

    72. Сергей Чикуенок
      15 мая 2011

      Здесь нет никакой энтропии: два поля однозначно говорит пользователю какие именно данные от него требуются. И как раз одно поле создаёт проблему: пользователи начинают жаловаться в в суппорт, что данные, которые ввели, система не принимает.

      И не надо перегибать палку насчёт электронного адреса: у него, в отличие от номера телефона, всего один вариант написания.

    73. Роман
      15 мая 2011

      Можно подробнее об этих проблемах?

      Мне казалось, что достаточно убедить пользователя ввести номер в федеральном формате, а не городском.
      А учесть +7 или 8 или ничего в самом начале и выкинуть все разделители это две строчки кода.

      О каких подводных камнях не знаю я и мои пользователи?

    74. Роман
      15 мая 2011

      И ах да. У электронной почты далеко не один вариант написания.
      John Smith .

    75. Роман
      15 мая 2011

      Парсер откусил часть адреса электронной почты (по RFC)…
      Наверное принял за тэг…

      Имелся в виду вариант: To indicate whom the message is intended for, a user can use the «display name» of the recipient followed by the address specification surrounded by angled brackets

    76. Сергей Чикуенок
      15 мая 2011

      Ну, например, некоторый пользователи писали номер в виде 8 10 123 456-78-90, некоторые не писали код, потому что подразумевали, что 495 или 499 указывать не обязательно.

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

      Каким образом? Заставить вводить данные в определённом формате (чего нет в моём варианте)?

      Имелся в виду вариант: To indicate whom the message is intended for, a user can use the «display name» of the recipient followed by the address specification surrounded by angled brackets

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

    77. Роман
      15 мая 2011

      > Каким образом? Заставить вводить данные в определённом формате (чего нет в моём варианте)?
      Написать об этом и привести пример.

      Определённость формата не нужна. Как я писал выше, для этого нужно ровно две строки кода.

      К слову сказать. Моя бабушка, которая для меня часть фокус-группы при юзабилити тестировании не сможет заполнить форму вида: +7 [код] [номер телефона]

      Она не знает, что +7 это альтернатива 8 и более правильные вариант для международного формата и понятия не имеет, что первые три цифры в телефонном номере какие-то особенные.

      Но вашу точку зрения я понял. Спасибо за ответы. Я подумаю об этом )

    78. Сергей Чикуенок
      15 мая 2011

      Написать об этом и привести пример.

      Я уже отвечал выше, что, приводя в качестве подписи фразу вроде «Например: +7 (123) 456-78-90» вы тем самым невольно навязываете пользователю определённый формат записи. В моём случае формата никого нет, я просто сообщаю в виде интерфейсных элементов, какие данные мне нужны.

    79. DiS
      15 мая 2011

      Итого, можно сказать, что вся пляска с бубном была ради того, чтобы написать не «Например: +7 (123) 456-78-90» и одно поле с умным парсером, а «+7» и два поля с адовыми скриптами, так? Скрипты хороши, но…

      Лично я вижу несколько неисправимых проблем юзабилити:
      1. Невозможно одним выделением и нажатием backspace/del удалить весь телефон
      2. Невозможно вставить скопипащенный откуда-то «+71234567890» или «871234567890», а у многих оно может храниться, допустим, в keepass’е и аналогах
      3. Невозможно скопипастить телефон из формы куда либо (редкий юзкейс, но тоже неустранимый косяк)
      4. Невозможно с зажатим shift’ом выделить весь телефон

      Отсюда вопрос — стоила ли игра свеч? Не лучше ли было оставить одно поле для 10 цифр, а перед ним написать +7, но разместить его с прозрачным фоном и границами поверх другого поля с маской, которую можно апдейтить по мере ввода?

      P.S. За неубираемое окно с входом/регистрацией надо расстреливать

    80. Сергей Чикуенок
      15 мая 2011

      DiS, всё, что вы описали, было бы справедливо для какого-нибудь колл-центра, где оператор с такими полями полдня работает. В моём же случае номер телефона нужно ввести всего один раз.

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

    81. Роман
      15 мая 2011

      P.S. За неубираемое окно с входом/регистрацией надо расстреливать

      Зря ты так про окно не убиваемое без фаербагоподобных тулз.

      Я по адресной строке увидел, что мой город определился правильно. Увидел, что купоны при этом показываются не из моего города (по станциям метро в надписях над купонами).

      Ну и закрыл нафиг этот сайт.

      Пять секунд на принятие решения это шикарно. Не будь окна я бы больше времени потратил.

    82. DiS
      15 мая 2011

      Сергей, я все же считаю, что вне зависимости от количества таких полей, поля должны быть юзабельны в первую очередь. Ведь в самом начале вроде как во главу угла ставилось удобство для пользователя, но от примененного скрипта кой-где удобство даже убыло, что очень плохо, плюс убились некоторые вещи, которые идут «из коробки». Это как выплеснуть младенца вместе с водой, раз уж беремся допиливать контролы, надо делать их вообще идеальными, чтоб не подкопаться. Это я усвоил на 200% когда делал замену стандартному select’у. Там юз-кейсов по клаве и клаве+мышь несметное количество.

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

    83. artuska
      28 сентября 2011

      Ситуация такая — у меня есть 4 инпута для ввода номера карточки. Сделал так, чтобы тоже фокус перескакивал и всё такое. Но дело в том, что пользователь уже однажды сабмитил эти поля и теперь у него работает autosuggest браузерский (тестил в Хроме, но это не имеет значения) — кликаешь по полю и выскакивают варианты, которые ты когда-то заполнял для этого поля. Кликаешь на один из вариантов и не срабатывает событие change (через jQuery задаю, все то же самое, что и в посте выше, .bind(‘keyup change’, …);), не перескакивает фокус на другое поле. Что делать и как этот autosuggest обрабатывать?

    84. 28 сентября 2011

      @artuska

    85. TiGR
      28 сентября 2011

      То есть,
      <input type=»text» name=»cc» autocomplete=»off» />

    86. artuska
      28 сентября 2011

      @TiGR
      Ну нет, обрубать автосаджест нельзя, это не годится, совершенно не юзерфрендли.

    87. 28 сентября 2011

      А хранить номера кредиток в открытом виде — юзерфрендли? 🙂

    88. artuska
      28 сентября 2011

      @Артём Сапегин
      Фигня, пусть хранятся. И вообще, это, считайте, просто пример — представьте себе другие поля, скажем, тоже номер карточки, но не банковской, а какой-нить другой. Проблема с onchange остается при автосаджесте.

    89. Вадим
      6 июня 2012

      Сергей, а неужели того, что нужно ввести мобильный телефон не достаточно, чтобы человек понял, что надо вводить его в какой угодно форме, но с «916» или «926». При этом если человек ввёл его без восьмёрки или без «+7», в виде 916 222-33-44, то понять, что он имел ввиду, кажется, несложно даже на уровне автоматического обработчика, ведь в любом городе России, если я не ошибаюсь, мобильный номер — это 10 цифр.

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