Нам нужна была работающая и удобная библиотека без зависимостей для маскирования ввода — и мы ее сделали. Через полгода с момента выпуска нулевой версии была выпущена версия 1.0 с многочисленными изменениями и улучшениями:

  • ограничение ввода чисел (целые и дробные, с возможностью ограничения интервала)
  • ограничение ввода дат (с поддержкой разных форматов, и также с возможностью ограничения интервала)
  • выделение общего алгоритма маскирования позволяет делать крутые кастомные штуки просто
  • API изменился минимально, но сильно расширился
  • и другие изменения под катом

В качестве предисловия


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

Всем не угодишь, да и не нужно. Но разнообразие не отменяет качество. Если продукт сделан грамотно, с умом, то пользователи найдутся. Кому-то нужен комбайн с миллионом взаимозаменяемых настроек под всевозможные платформы с интеграцией во все фреймворки. Мы же за минимализм с возможностью расширения.

Нужно больше примеров кода и демо. Страница проекта и документация полностью переработаны.

Нужны рабочие варианты из коробки. Добавлены новые маски и примеры использования.

На мобильных устройствах в настоящее время заставить работать курсор правильно невозможно. Буду рад, если докажете обратное. Все библиотеки, изменяющие ввод стоят перед выбором: сделать удобно для десктопа или чтобы работало на мобильных устройствах. Мы выбрали первое. Чтобы работало на мобильных устройствах необходимо учитывать не только платформы и многочисленные браузеры, но также широкий ассортимент виртуальных клавиатур со своими особенностями. Короче говоря, если хотите чтобы работало — не трогайте курсор при вводе. Серьезно, проверьте сами — так и делают (хотя может они тупо забили). В IMaskjs мы сделали достаточно сложное позиционирование курсора для удобства на десктопе с небольшим хаком под мобильные платформы. Хак известный и заключается в том, чтобы устанавливать курсор с небольшой задержкой после ввода — получилось не сильно лучше, но где-то заработало.

Что нового


Библиотека увеличилась в объеме в 2 раза в основном за счет нового функционала. Наиболее значимым дополнением стали маски для работы с числами и датами:

var numberMask = new IMask(inputElement, {
    mask: Number,
    min: -10000,
    max: 10000,
    thousandsSeparator: ' '
});

var dateMask = new IMask(inputElement, {
    mask: Date,
    min: new Date(2000, 0, 1),
    max: new Date(2020, 0, 1),
    placeholder: {lazy: false}
});

> Демо тут

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

var groupsMask = new IMask(inputElement, {
  mask: '19YY.MM.DD',

  // описание групп
  groups: {
    // общее описание группы
    YY: {
      mask: '00',
      // возможно использование валидатора
      // validate: function (value, group) {}
    },

    // но можно создавать группы из интервала значений или перечислений
    MM: new IMask.MaskedPattern.Group.Range([1, 12]),
    DD: new IMask.MaskedPattern.Group.Range([1, 31])
  }
});

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

В процессе рефакторинга также были выделены модель и представление. Модель предоставляет все возможности маскирования без UI. А представление “склеивает” dom-элемент и модель маски, обеспечивая реагирование на события и обновление элемента. Также довольно просто добавить собственные обработчики событий, например, для поддержки старых браузеров (из коробки поддерживаем IE11+).

Планы на будущее


Ближайшие планы — сделать возможность использовать несколько масок, меняющихся на лету, например, ввод телефона и email. Также возможно появятся плагины под любимые фреймворки.
Мы призадумались, а зачем мы вообще все это делаем, присмотрелись к cleave и text-mask — они ведь тоже хороши в принципе. Конечно каждый выберет свое, но нам наша маска нравится больше.

Несмотря на то, что библиотека неплохо работает в проде с несколькими сотнями тысяч активных пользователей, по-прежнему нет нормальных тестов и 0 issues на github))) Стыд и позор, нет слов. Исправляемся.

Комментарии (22)


  1. virtusIV
    24.09.2017 16:03
    +1

    >40кб в минимизированном виде — многовато как-то :(


    1. burfee Автор
      24.09.2017 16:13
      +1

      38.4кб если быть уж таким точным. Но кого сейчас это волнует?
      Какое-то время я пытался максимально уменьшать объем бандла, использовал var вместо let и const, писал свои хелперы и т.д. В результате напоролся на несколько неприятных багов, и решил что ну его подальше — надежность важнее. Тем не менее, я насколько смог подтюнил сборку, т.е. размер имеет для нас значение, но не первостепенное.
      На самом деле большинство жира — это объявления свойств, core-js импорты и наверно декоратор ) Это плата за гибкость, надежность и удобство использования. Можно было сделать все топорно неизменяемым конечно.
      Также все это дело отлично жмется gzip-ом и получается в районе 10-15кб.


      1. Hixon10
        24.09.2017 23:03
        +3

        Но кого сейчас это волнует?


        Да много кого, на самом деле. Если бы проблема размера не была бы актуальной, вряд ли бы появлялись такие проекты, как preactjs.com и zeptojs.com


        1. burfee Автор
          25.09.2017 08:37

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


      1. tamtakoe
        25.09.2017 01:43

        Действительно, кого это волнует! Десяток таких либ и вот уже полмегабайта. Это неприлично много для подобной штуки, учитывая, что половина кода — не несет смысловой нагрузки.


        1. burfee Автор
          25.09.2017 08:39

          Я вижу смысл в этом коде и отвечаю за принятые решения. Какую половину вы предлагаете выкинуть?


          1. SilverFerrum
            25.09.2017 13:12

            Попробуйте сделать несколько сборок, например, без дат.


            1. burfee Автор
              25.09.2017 13:12

              Да, как вариант распиливать сборку. Когда-нибудь доберусь.


          1. tamtakoe
            25.09.2017 18:38

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


  1. Lodin
    25.09.2017 01:52

    Включать полифиллы в библиотеку — моветон. Полифилл должен подключать конечный пользователь, который точно знает, для каких устройств и браузеров он пишет приложение. А библиотека должна только предоставлять исключительно логику.


    Почему бы просто не указать в readme, какие полифиллы могут потребоваться этой либе?


    1. burfee Автор
      25.09.2017 08:50

      Мне ближе идея — взял и работает. Мы же пользуемся менеджерами пакетов, чтобы не думать о зависимостях?
      Согласен с тем, что в крупных проектах собирать полифилы по всем зависимостям глуповато. Но не удобно также постоянно думать про полифилы. Это должно решаться на этапе сборки и обычно крупные проекты используют babel, сборщик и полифилы. Также обычно библиотека лежит рядом с полифилами, и если использовать import руками, то по идее сборщик должен их ушатать и положить в одно место, т.к. core-js общий. В общем виде нельзя назвать решением, но попробовать можно.
      В моем случае полифилов не так много, а для большинства все таки важнее «коробочность».


  1. igontarev
    25.09.2017 08:25

    есть несколько проблем
    1) наверное самая частая, это проблема с автокомплитом, должно быть +79613076660
    image
    2) я не могу ввести только 4х значное число, хотя максимум 10к, но скопипастить я могу 10000
    image

    на счет размера тоже думаю, что не критично, нравится, что это самостоятельная библиотека, которую можно будет интегрировать в react,vue,ng
    надо будет так же протестить именно на этих фреймворках, т.к. скорее всего там и будет использоваться ваша маска


    1. burfee Автор
      25.09.2017 08:29

      Картинки не вижу, отвечаю на угад.
      1) фиксанул вчера
      2) скорее всего, вы имели ввиду, что можете ввести только 4х значное число. Да, все верно. 10000 вы тоже можете ввести от руки и вставить. Но любое другое 5ти значное число больше 10000, поэтому ввести нельзя.
      За позитив спасибо.


      1. igontarev
        25.09.2017 10:26

        1) частично фиксанули, при первом вводе (подстановке из автокомплита хром) все ок, а вот уже последующие подстановки такая же ошибка как и была, т.е. я стер телефон и снова пытаюсь подставить телефон, он уже неверный, правильный — +79613076660
        image


        1. burfee Автор
          27.09.2017 09:45

          поправил, спасибо за баги :)
          Когда сравниваешь с аналогами, такие случаи в глаза не бросаются, т.к. полно других косяков просто с обычным вводом. Автокомплит я вообще поддерживать даже пока не планировал, если честно не надеялся что доберусь до него, столько было других ньюансов))) Но видимо пора повышать планку. Набрасывайте еще, если будет, лучше в github или в личку.


  1. Aetet
    25.09.2017 08:34

    Шел 2017 год, а ctrl+z с masked input до сих пор не работают.


    1. burfee Автор
      25.09.2017 09:05

      :) все верно. Кто-то пытается костылить, но большинство вендоров просто забивают.
      Я честно попытался, но оказалось, что даже firefox и chrome обрабатывают ctrl-z по разному. Нет общего адекватного API на undo/redo, остается писать костыли. Возможно что-то сделаю в низком приоритете, но пока остановился на том, что сама маска не ломается — и ладно.


    1. igontarev
      25.09.2017 10:29
      +1

      Шел 2017 год, а мы все еще не можем нормально анимировать высоту на css, флексы все еще с багами, отдельное спасибо сафари, в общем так и живем :)


  1. UksusoFF
    25.09.2017 13:20
    +2

    С телефонами как всегда боль: хочу вставить +79271234567 из буфера, а получаю +77927123456.


  1. ozonar
    26.09.2017 12:36

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


    Я понимаю, что сделано это в угоду февралю, количество дней которого зависит от признака високосности года, но это совершенно неудобно


    1. burfee Автор
      26.09.2017 13:20

      Согласен, что это может быть или показаться на первый взгляд неудобным, но есть простые способы сделать как вам нужно — можно передать свой валидатор. Здесь не может быть общего решения и февраль тут не причем. Это сделано по нескольким причинам:

      • упрощение логики проверки даты. Если вам нужно проверять февраль или каждую вторую субботу пятого месяца — используйте свой валидатор — свойство validate. В сочетании с событием accept вы, например, можете добавлять подсказки под полем и пр.
      • формат даты может быть совершенно произвольный и единственный общий способ проверки даты — это когда значение полностью заполнено и может быть преобразовано к формату даты.


  1. Suliman
    27.09.2017 14:36

    Всё плагины масок которые есть на просторах интернета имеют баги на Андроиде 7, и этот плагин не исключение, хотя он показал себя лучше чем все остальные. Вот короткая видео запись поведения плагина на 7 андроиде — drive.google.com/open?id=0B1xWMXxRmlVjTTRkVnlVYThxU2c

    Есть два глюка:
    1. Если после того, как полностью ввел номер, набирать еще цифры, то номер может смещаться (цифры перезаписываются)
    2. Полностью удаляем то что ввели. Доходим до когда страны (+7) нажимаем «Удалить» и 7 снова появляется, удаляем, удаляем еще раз — 7 снова появляется.