Получил я достаточно стандартное задание: фильтровать вводимые юзером символы в input, т. е. пользователь может ввести в строку набор цифр и букв, например, '5s68d.4r55e.6t5', а на сервер я должен отправить корректное для сохранения сумму в рублях — '568,455' (рублей).

Справился я с заданием достаточно быстро, повесив на input событие focusout, но у моего решения был ряд важных недостатков: где в данном примере заканчивается сумма в рублях и начинаются копейки? Если пользователь введет несколько минуcов (отрицательные значения тоже корректны в данном случае), то какой из минусов считать началом строки? И так далее.

Поэтому появилась вторая версия скрипта с регулярными выражениями на событие keyup:

$(e.curretTarget).val(($(e.currentTarget).val()).replace( /[^0123456789.-]/, '' ))

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

По этой причине старший товарищ дал указание написать функцию на событие keypress. Через 30 минут уже третий вариант функции был готов и имел он примерно такой вид:


function()
{
    return this.each(function()
    {
        $(this).keydown(function(e)
        {
            var key = e.charCode || e.keyCode || 0;
            // allow backspace, tab, delete, enter, arrows, numbers and keypad numbers ONLY
            // home, end, period, and numpad decimal
            return (
                key == 8 || 
                key == 9 ||
                key == 13 ||
                key == 46 ||
                key == 110 ||
                key == 190 ||
                (key >= 35 && key <= 40) ||
                (key >= 48 && key <= 57) ||
                (key >= 96 && key <= 105));
        });
    });
};

Код взят со steckoverflow, но мой код мало чем отличался от примера выше.

Всё выглядело красиво — пользователь не видит вводимых чисел и курсор не перебрасывает в конец строки, но, как оказалось, радовались мы рано. Если смотреть на keycode клавиш на разных операционных системах (mac, linux, win), то они имеют некоторые различия, а если к этому прибавить то, что не у всех маков есть numPud и, следовательно, числа вводятся с зажатым шифтом и также цифры могут вводится с виртуальной клавиатуры. В итоге получается код во много раз больше, чем последний пример.

В итоге я написал скрипт на событие keyup и он заработал с единственным недостатком — пользователь на секунду видит вводимое число. По понятным причинам я не могу выложить скрипт, но эта ситуация побудила меня написать новый скрипт, который я выложил на github. Я сделал скрипт более универсальным — сейчас он больше рассчитан на передачу значения в DOM-элементы, чем на модификацию введенного значения в input (это будет следующий шаг).

На момент написания статьи в функцию можно передать несколько параметров:

  • forDisplay: true, // для отображения в дивах
  • classOfDomElement: '',// классы DOM элементов через запятую без пробелов
  • idOfDomElement: '',// id DOM элементов через запятую без пробелов
  • //forInput: false,
  • forSave: false, // function forSave — возвращает только цифры с точкой
  • negative: false,
  • afterPoint: 2, // количество цифр после точки
  • showPoint: true, // показывать точку до ввода копеек
  • currency: 'руб.' // валюта

Спасибо за внимание!
Поделиться с друзьями
-->

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


  1. Dolios
    17.10.2016 16:08
    +2

    пользователь может ввести в строку набор цифр и букв, например, '5s68d.4r55e.6t5', а на сервер я должен отправить корректное для сохранения сумму в рублях — '568,455' (рублей)

    Для тестового задания пойдет, а в жизни такой подход применять крайне опасно. Что если ваша 't', это опечатка, при попытке нажать '6'?


  1. Delphinum
    17.10.2016 16:09
    -2

    По этой причине старший товарищ дал указание написать функцию на событие keypress

    А старший товарищ не дал линейкой по рукам и не предложил готовое решение, коих в интернете пачка?


  1. Nadoedalo
    17.10.2016 16:33
    -4

    jsfiddle, тесты — это раз
    ужасный английский в readme — это два
    ужасная организация кода — это три
    PS собственно сам денежный формат с кучей фич влезает в 27 строчек кода(на 12 кейсов) + «обёрточная мишура» на 25 строк + сеттер/геттер позиции курсора(необходимо только для FF, возможно уже и пофикшено). Выложить код к сожалению не могу, но однозначно есть способ сделать это лучше.
    PSS далеко ушли от собственно говоря реализации денежного формата. В идеале это две функции «на выход» — одна собственно которая превращает строку в денежный формат, другая — умеет превращать DOM-элементы в тип денежного формата.


    1. tarasikgoga
      17.10.2016 17:04
      +4

      ужасная организация кода — это три

      буду рад если подскажите как организовать код лучше


      1. Nadoedalo
        17.10.2016 18:47
        -6

        Дай человеку рыбу и он будет сыт один день…
        Посмотрите что-ли курсы в интернете, попишите например на Vanilla JS framework(или на любой другой устоявшейся либе/фреймворке). Да хотя бы гляньте на код TODO app'a.
        Просто бесполезно объяснять что не так с кодом. Это приходит со временем или вообще не приходит.


  1. AlexPu
    17.10.2016 16:42

    Я припоминаю, что делал нечто подобное и именно с использованием keydown. никаких проблем с маками не имел (несколько разработчиков и практически все менеджеры были вооружены именно ими). Более того, я не просто фильтровал вводимые символы но и попутно их форматировал — все нормально было… помню, что код чамому очень понравился, что для меня очень редкое явление… но… не сберег… я вообще пофигистично отношусть к своему, да и к чужому коду… надо будет — еще напишу…


  1. foxyrus
    17.10.2016 17:20
    +1

    А вставка из буфера обрабатывается?


    1. tarasikgoga
      17.10.2016 23:45

      да


  1. barkalov
    17.10.2016 17:30
    +5

    <input type="number">
    


    1. ahmpro
      17.10.2016 22:13
      +3

      Дополню, что у input есть еще аттрибут pattern, в который ставим регулярку и радуемся, правда это не для старых браузеров.


  1. bacsus_beacks
    17.10.2016 17:54
    -1

    <input type='number' step='0.01'>
    And everybody is happy.
    Nah?


    1. Nadoedalo
      17.10.2016 18:55
      -4

      Как и обычно встроенные инпуты работают не идеально с минимальными возможностями исправить поведение. Понятно что нативные элементы работают _лучше_(в широком смысле этого слова, например в разных окружениях), но не всегда так как хотелось бы. Лично мне приходилось писать и textarea и select'ы и эмулировать таблицу div'ами просто потому что так — гибче. Ну и типичный пример — ужасно-некрасивый скролл внутри документа от которого дизайнеры плюются а пользователей коробит.

      Вот если бы можно было «внутрь» нативных элементов залезать тогда да(на самом деле уже «можно» с помощью Webcomponents/Shadow root вот этого всего), но пока что — приходится извращаться.


  1. Zavtramen
    17.10.2016 17:56
    +6

    где в данном примере заканчивается сумма в рублях и начинаются копейки?

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


  1. cjbars
    17.10.2016 18:21
    +7

    Я может быть глупость спрошу, но зачем все эти переподвыпернверты? Выше написали поо input type number, а обо всем остальном надо оповестить пользователя. Если вы хотите чистить данные перед отправкой их на сервер, то мне кажется это глупо, ибо данные можно отправить и без инпутов.


    1. jbubsk
      17.10.2016 22:59
      -5

      не глупость


  1. muxa_ru
    17.10.2016 22:00

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


    1. tarasikgoga
      17.10.2016 22:11

      обрабатывается


  1. Shannon
    18.10.2016 02:18

    По этой причине старший товарищ дал указание написать функцию на событие keypress.

    Код взят со steckoverflow, но мой код мало чем отличался от примера выше.

    Всё выглядело красиво — пользователь не видит вводимых чисел и курсор не перебрасывает в конец строки, но, как оказалось, радовались мы рано. Если смотреть на keycode клавиш на разных операционных системах (mac, linux, win), то они имеют некоторые различия, а если к этому прибавить то, что не у всех маков есть numPud и, следовательно, числа вводятся с зажатым шифтом и также цифры могут вводится с виртуальной клавиатуры. В итоге получается код во много раз больше, чем последний пример.

    Разница есть. Ваш товарищ дал вам указание использовать keypress, а в примере keydown, в этом и есть разница

    У keypress будет код финального символа, а у keydown будет выдан keyCode клавиши плюс флаги нажат ли shift, alt и т.д, раскладка игнорируется. Можно проверить в живую: https://learn.javascript.ru/keyboard-events

    То есть если у нас keypress и нажать 1 мы получим keyCode=49, с зажатым шифтов получим уже keyCode=33

    Если у нас keydown и нажать 1 (хоть с шифтом, хоть с чем угодно) мы всегда получим keyCode=49 на любой клавиатуре, на любой ОС, хоть на виртуальной клавиатуре планшета

    Аналогично даже если текущая раскладка русская, нажимая точку получаем 'ю', но keyCode всё равно будет 190 (как и у просто точки), а не 1102 (код символа 'ю')


  1. dkameleon
    18.10.2016 08:21
    +2

    Не надо изобретать костыли и решать за пользователя, что он хотел ввести.
    Хотите ограничить его в отправке неправильных данных — type=number + pattern

    Велосипед focusout не едет в ФФ.

    Если смотреть на keycode клавиш на разных операционных системах (mac, linux, win), то они имеют некоторые различия, а если к этому прибавить то, что не у всех маков есть numPud и, следовательно, числа вводятся с зажатым шифтом и также цифры могут вводится с виртуальной клавиатуры. В итоге получается код во много раз больше, чем последний пример.

    А завтра выпустят новый модный молодежный браузер и вам опять перепроверять и переписывать костыли.


    1. tarasikgoga
      18.10.2016 08:27

      Велосипед focusout не едет в ФФ.


      Не знаю про какой вы велосипед, но мой код работает в ФФ.


  1. RubaXa
    18.10.2016 11:31

    Зачем это всё? Уже много лет есть куча готовых и проверенных решений для jQuery.


  1. RoseWoodsAlloy
    18.10.2016 12:23

    Занимательная статья.
    Занимательно в ней все. Особенно меня впечатлили steckoverflow и numPud…
    … Представляете… целый пуд нумов в английском переполнении немецкого штекера.


  1. Taller
    18.10.2016 17:54

    1,234.5
    1234.501
    1.234,50
    1234,5
    1234-50
    а такие суммы одинаковы или нет? =)

    В свое время решал подобную задачу на Java и это был основной вид сумм в документах от разных бухгалтерий, которые приходилось распознавать. Были и другие, менее популярные.


  1. Zhuravljov
    19.10.2016 06:49

    Получил я достаточно стандартное задание: фильтровать вводимые юзером символы в input

    Задание то стандартное, но подошли вы к нему не с той стороны. Если просто потренироваться — возможно. А для реальных приложений так делать не стоит.


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


    А если и нужны ограничения на этапе ввода, то проще type="number" или атрибут pattern.


  1. LakeVostok
    19.10.2016 08:26

    простая функция фильтра инпута от букв, т.е. вводивые буквы не появляются в инпуте. Вешается на oninput
    function(){
    event.target.value = event.target.value.replace(/[^0-9\.]/g,'');
    });
    http://codepen.io/vasyatopor/pen/kkAmZW?editors=1010
    Дальше сами модернизируйте


    1. tarasikgoga
      19.10.2016 08:27

      Если ввести 5555, а потом ввести букыу вот так 55а55, букву вырежит а курсор перекинет в конец строки


  1. cjbars
    19.10.2016 15:50

    А если ввести 5e3 рублей?
    Ведь это верная запись = 5000 рублей
    Что прикажете делать с этим?