Как создать маску ввода для мобильных устройств

Как создать маску ввода для мобильных устройств

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

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

Мне потребовалось около 10 секунд, чтобы вспомнить, что события клавиатуры работают по-разному для разных устройств. Оказывается, найти решение для маски ввода которое будет работать в Android, iOS и Google Chrome было не так просто.

Знакомство с событиями клавиатуры

Позвольте мне познакомить вас с событиями клавиатуры. Они запускаются при нажатие на физическую или виртуальную клавишу:

  • Нажатие клавиши вызывает событие keydown
  • При нажатии на печатную клавишу (цифра, символ, буква) запускается событие keypress
  • После отжима клавиши срабатывает событие keyup

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

    возвращает числовое значение, связанное с конкретно нажатой клавишей, независимо от того, находится ли этот ключ в нижнем регистре или в верхнем регистре указывает значение ASCII символа, связанного с нажатой клавишей. Это свойство отличается для нижнего и верхнего регистра возвращает значение нажатой клавиши. Если вы нажмете клавишу в нижнем регистре, это свойство вернет нажатую букву, а не кодовое представление клавиши возвращает числовой keyCode нажатой клавиши или charCode для буквенно-цифровой клавиши. возвращает строковое представление нажатой клавиши. представляет собой физический ключ на клавиатуре. Это свойство возвращает значение, которое не изменяется с помощью раскладки клавиатуры или состояния клавиш-модификаторов.

Android проблемы

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

Но нет, дело в том, что Android-клавиатура не так проста. Когда я добавил событие keypress и показал свойства keyCode, я немного удивился: в Android не было события keypress.

Нажатие клавиши А верхнем регистре привело к следующим свойствах keyCode в браузерах:

Я быстро узнал, почему Chrome Mobile не запускал событие: это событие отмечено как Legacy в стандарте DOM-Level-3. Ладно, это еще не конец света. Я все еще могу использовать одно из двух оставшихся событий: keydown или keyup.

Я тестировал оба события. Нажатие А верхнего регистра привело к следующим keyCode:

Ок, событие сработало, но что это был за код 229? Откуда он? После некоторых поисков я узнал, что функция замены или другие события могут последовать за событием keydown и аннулировать его. Некоторые устройства могут возвращать код 0, Unidentified или даже пустоту, но причина одна и та же.

И, что более интересное, отключение функции автозамены не решает проблему.

Чтобы помочь мне решить, какое свойство я должен использовать, я перешел в режим тестирования и создал следующую таблицу. Я знаю, что некоторые из следующих свойств устарели, но что я могу вам сказать? Я был укушен тестирующей ошибкой!

Я сам тестировал эти события на своих устройствах, но если вам нужна дополнительная информация о свойствах, версиях и совместимости, вы можете найти ее на сайте MDN.

Как видим, на Chrome Mobile ни одно свойство не работает. Без паники! У меня есть обходное решение. Да, после большого поиска и тестирования я нашел хорошее решение для получения ключа, который был нажат как в iOS, так и в Android. Но прежде всего, давайте поговорим о еще одном событии и о порядке запуска всех этих событий. Потерпите меня.

Немного о событии ввода

Событие ввода запускается каждый раз, когда мы что-то вводим, это означает изменение значения элемента <input>, <select> или <textarea>. Событие запускается во всех перечисленных браузерах. Но оно не возвращает те же свойства, что и клавиатурные события, потому что это событие не запускается с помощью действий клавиатуры. Но оно вызывается в результате этих действий. Вы можете увидеть его свойства посетив сайт Mozilla Developer Network

Срабатывание события

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

keydown > keypress > input > keyup

Обратите внимание, что запуск события keydown изменяет значение, и после запускает событие ввода. И только после отпускания клавиши происходит событие keyup. Также помним, что событие keypress может не запускаться на некоторых устройствах.

Что же делать с Android keyCode 229

Теперь, когда вы знаете о событие ввода и порядок событий, давайте поговорим об обходном пути для Android 229 бага.

Вот как найти нажатую клавишу. Когда событие keydown запускается, введенное значение не изменяется, поэтому вы должны его сохранить. Однако, если событие не отменено, значение ввода изменится. Это вызывает событие ввода и создает новое значение. Поэтому, если вы сохранили входное значение перед изменением, вы можете сравнить его с новым после изменения.

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

Если вам действительно нужно использовать charCode, вы можете получить символ, который является разницей между oldValue и newValue и использовать функцию charCodeAt.

Требования к маске ввода

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

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

Итак, давайте проанализируем требования:

  • Разрешено только 16 цифр
  • Добавьте разделитель “ ” (пробел) для каждой группы из 4 цифр
  • Должна работать с вставленными значениями
  • Если в вставленном значении имеются недопустимые символы, маска должна удалить эти ключи и замаскировать только оставшуюся цифровую часть

Обратите внимание, что маска ввода должна обрабатывать полное значение всякий раз, когда оно изменяется вместо последнего вставленного ключа. Мы не собираемся использовать разницу между старым значением и новым для проверки ключей, но мы все равно будем использовать старое значение. Зачем? Продолжайте читать!

Давайте сделаем то, что мы уже знаем: добавим события onkeydown и oninput, и сохраним старое и новое значение.

Маска ввода кредитной карты: Давайте кодить!

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

Но для чего нам разбирать значение, а затем снова маскировать его?

Итак, представьте себе следующий сценарий: поле ввода имеет 1111 2222 3333 значение, а затем пользователь выделяет все значение и заменяет его новым, например: 444455 55. Это новое значение маскируется независимо от его структуры. Оно всегда будет адаптироваться к маске, очищая все символы, которые не являются цифрами.

Функция маски

Эта функция должна добавлять разделители между каждой группой из четырех цифр. Чтобы найти где их ставить, мы проходим по всем символам значения и если индекс символа делиться на 4 без остатка, то ставим разделитель. Для этого мы будем использовать оператор модуля (Остаток от деления). Подробнее об операторе модуля

Вот так выглядит функция:

В данной функции также исключен нулевой индекс, чтобы не ставить после него разделитель

Пример: если новое значение = 11112, вычисление будет:

Функция разбора/очистки

Это простая функция. Она подготавливает значение для маскировки. Она заменяем все символы кроме цифр на “” (пустое значение)

У нее будет такой вид:

Лимит символов

Маска кредитной карты допускает только 16 цифр. Чтобы ограничить ввод символов, мы собираемся создать новое регулярное выражение и проверять, соответствует ли значение регулярному выражению. Если true, значит его нужно снова замаскировать и распечатать. Если false, значение оставляем без изменений.

Ваш код должен выглядеть так:

Если вы проверите маску, вы увидите, что она работает.

Позиция курсора

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

Давайте поговорим о двух свойствах поля ввода:

    : получить начало выбранной части поля текста : Получить конец выбранной части поля текста

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

Итак, вот что мы сделаем. Создадим новую переменную и назовем ее oldCursor.

Внутри события keydown сохраним позицию курсора с помощью selectionEnd перед вставкой нового значения. Теперь у нас есть как oldValue, так и oldCursor, содержащие информацию о значении и позиции курсора, соответственно, до их изменения.

Создадим новую функцию, которая проверит, сколько разделителей имеет строка, пока не достигнет позиции курсора. В этом примере я назвал его checkSeparator.

Эта функция вернет округленное значение положения / интервала (4) + 1, где +1 — компенсация разделителя. Другими словами, возвращайте +1 для каждого разделителя перед позицией.

Я построил формулу для установки новой позиции курсора на основе текущей позиции курсора, oldValue и newValue.

Теперь, когда у нас уже есть новая позиция курсора, вы можете применить ее к вводу с помощью: setSelectionRange(start, end) метода. Он устанавливает выбор между начальным и конечным параметрами. Если начальные и конечные значения совпадают, между выбором не будет интервала.

Типы клавиатур в мобильных приложениях

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

Был ли я прав? Нет, я ошибся! Числовой тип ввода не поддерживает наш разделитель, и он автоматически запускает проверки, которые мы не хотим. Итак, как я решил это? Ну, я решился на решение, которое я пытался избежать, потому что это не так элегантно. Я попробовал тип «tel».

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

Да, это сработало! И самое главное, он работает на устройствах Android и iOS.

Посмотрите, как оно прекрасно работает здесь:

Вывод маски ввода

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

Мое приключение с маской было долгим, напряженным и болезненным процессом попыток и неудач, исследований и постоянных повторений. Узнав, что нет простого способа достичь некоторых свойств клавиатурных событий, могу вам сказать, что у меня сейчас большое уважение к ним. Они требуют вашего полного внимания, независимо от того, зачем они вам нужны. Поэтому я должен признать, что каждый раз, когда кто-то спрашивает меня о событиях с клавиатурой, я отношусь к этому очень серьезно.

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

Ссылки и дополнительная литература:

Данная статья является переводом статьи “JavaScript Events Unmasked: How to Create an Input Mask for Mobile” и все права оставляются в автора оригинала

📎📎📎📎📎📎📎📎📎📎