null

История о форматировании цены (часть 2)

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

Цель осталась прежней - повысить качество восприятия информации пользователем.

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

Через какое-то время родился такой код

price.toString().replace(/(\d)(?=(\d{3})+$)/g, '$1 ');

 

Результат был следующий

 

Всё было прекрасно и теперь такого же эффекта захотели не только для обычного текста, но и для полей ввода.

Лишь взглянув, на это становится понятно, что добавление пробелов в имеющийся value input'а испортит и валидацию и сохранение формы на сервере, так как они оба ожидают на свой вход число.

Первой идей было, решение этого вопроса с помощью прятания css'ом оригинального текста, который вводится в input и показыванием 'фиктивного элемента' с правильным содержанием.

Здесь я вспомнил о псевдоэлементах :before и :after и пошёл писать css правила добавляющие input'у :after с абсолютным позиционированием и нужными положением, чтобы выглядеть также как и исходное число. На этом этапе, после того как никакой псевдоэлемент не появился,  было выявлено, что :after и :before могут быть лишь у элементов контейнеров, input к которым не относится. Не долго думая, поле ввода было обернуто в тег span, а правила для псевдоэлемента были заданы уже ему.

Теперь пришла очередь добавлять javascript код, который меняет content у элемента на основе того, что было введено в input. В этом месте были получены новые знания. :after не просто так носит название псевдоэлемента, а секрет названия кроется в том, что он не является частью dom модели. Из этого выходит что манипулировать им в js мы не можем.

Оговорочка: JS не был бы так 'прекрасен', если бы не было путей обойти это, но для меня такие манипуляции выглядели слишком безобразно, чтобы использовать их самому.

 

Следующим шагом стал отказ от :after, добавление ещё одного тега span, который будет описанными выше методами спозиционирован, а внутрь javascript'ом положится новое отформатированное значение.

Здесь я наткнулся на очень неприятный кейс. Чтобы убрать отображение исходного числа, цвет текст внутри input'а был выбран таким же как и его фон. С невидимым текстом, вподарок был получен баг. Вертикальная линия (каретка) приняла также цвет фона. Из-за этого её не было видно...

Возможностей стилилазции цвета курсора, я к сожалению не нашёл. После чего было решено отказаться от попыток замены значения поля иным элементом. Примерно в это время я узнал, что для описания такого форматирования есть достаточно устоявшийся термин в английском языке - 'thousand separator'.

В итоге был изменено поведения события onChange поля. По мимо сохранения значения в state объекта к нему добавилась логика форматирования. Это также потребовало изменений в обработке валидации и сохранения объекта, но усилий, как оказалось, потрачено было меньше чем на поиски и попытки решить задачу иначе.

В результате, мы получили рабочий форматированный input. Но ложка дегтя нашлась и здесь. Был обнаружен баг, заключающийся в следующем:

Если пользователь ввёл число, а потом решил переместить курсор на середину и ввести/исправить несколько цифр, то у него это не получится, так как после каждого введённого символа, каретка уезжает в конец строки. Причиной этого, судя по всему, является динамическое изменение 'value' у поля ввода.  Решение этого бага мне пока неизвестно, но во время поиска была найдена, библиотеча, которая призвана для решения проблем с форматированием чисел, включая и добавление 'thousand separator'а'. Посмотреть её можно здесь:  демо и исходный код.

А есть ли у вас предложения о том, как лучше решить эту задачу?

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