Яндекс.Метрика

    Песочница

    Альтернативный взгляд на плагин для маски ввода чисел и не только в input и textarea

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

    Подробности под Хаброкатом.

    Дано:
    Большой Web-проект, для UI'ой части которого используются backbone.js, underscore, jQuery, jQueryUI.
    В связи со спецификой проекта, требуется применение масок и валидирование полей ввода (input, textarea, etc.), в зависимости от типа (int, float, price, phone, text, combobox). С первого взгляда ничего архисложного в данной задаче нет и можно обойтись чем-нибудь из целого моря плагинов, которыми нас так любезно снабжает jQuery. Однако позже к данным типам добавились date (сделанный на основе jQuery DatePicker) и текстовый редактор (tinyMCE). На подходе было еще несколько типов, спецификация к которым только создавалась, и далеко не факт, что нам бы удалось найти готовое решение.
    Вот тут-то вопрос валидации встал более остро. Количество используемых плагинов росло как на дрожжах, и начинало настораживать. В-общем, устав от постоянных отмашек «Мы сделали вот так, потому, что вот этак мы сделать не можем – это стандартное поведение плагина», было решено создать собственную систему валидации.

    Найти:
    Создать собственный модуль валидации, построенный на основе используемых в проекте технологий.

    Решение:
    В основу модуля была положена модель backbone.js, поскольку он имеет уже встроенную проверку валидации данных, было бы глупо отказываться от такой возможности.

    Я не буду описывать всю структуру проекта, люди, которые работали с backbone.js без труда разберутся, кто нет, легко найдёт на Хабре вводные статьи.

    Итак, в нашем common View был создан метод validation(), который принимает определенные параметры валидации и передает их в кастомный модуль Validation.js (для него была создана отдельная папка Utils, которая лежит с остальными common элементами), создавая на его основе объект модели:

    validate: function (params) {
       if (_.isArray(params.items) && params.type === 'form') {
          _.each(params.items, function (item) {
             new core.utils.Validation(item);
          }, this);
          new core.utils.Validation(params);
       } else {
          new core.utils.Validation(params);
       }
    }
    


    Все кастомные Views в проекте наследуются от common View, следовательно, содержат в себе метод validation(). Вызывается валидация следующим образом: в контролере после рендеринга определенного View сразу же вызывается кастомный метод validationFields, который разный для всех Views, например:

    this.getView(‘SomeView’).render({
       'someParams':  someValue,
       …	
    }).validateFields();
    


    Сам метод validationFields может выглядеть следующим образом:

    validateFields: function () {
       this.validate({
          id: "description",
          type: "text",
          required: false,
          maxLength: 500,
          separate: true
       });
    }	
    


    Мы сообщаем нашему плагину, что
    id: «description» – требуется валидировать поле с id = «description»;
    type: «text» – тип вводимых данных – text (т.е. разрешены буквы, числа, знаки препинания и т.д.);
    required: false – поле является не обезательным для заполнения, не выдает никакого сообщения об ошибке вводимых данных, однако позволяет водить лишь, что что определено для типа text (т.е. маска остается работать);
    maxLength: 500 – максимальная длина текста 500 символов;
    separate: true – обрезать каждый символ после пятисотого.

    Что бы имеем в самом модуле:

    text : function() {
        $("#"+this.id).live("paste", $.proxy(this.maskPasteText, this));
        $("#"+this.id).live("keyup", $.proxy(this.validateText, this));
        $("#"+this.id).live("blur", $.proxy(this.finishText, this));
    },
    


    Тут мы навешиваем методы валидирования и маски на события paste, keyup, blur.

    maskPasteText: function(event) {
       var context = this;
       setTimeout(function() {
          context.validateText(event);
       }, 0);
    },
    


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

    finishText: function(event) {
       this.validateText(event);
    },
    


    Собственно, а вот и сама функция валидирования текста:
    validateText: function(event) {
       var value = $("#"+this.id).val(),
             selectionStart = event.target.selectionStart,
             selectionEnd = event.target.selectionEnd;
       if (this.maxLength && this.separate) {
          value = value.slice(0, this.maxLength);
          $("#"+this.id).val(value);
          event.target.selectionStart = selectionStart,
          event.target.selectionEnd = selectionEnd;
       }
       this.bind("error", $.proxy(this.showError, this));
       this.validate = this.validations()[this.type];
    
       this.hideError();
       this.set({
          currentValue: value,
          required: this.required,
          errorMessage: this.errorMessage,
          minLength: this.minLength,
          maxLength: this.maxLength
       });
    },
    


    Понятно, что для других типов данных, например, int, float еще следует добавить маску ввода, для этого на событие keypress вешаем функции:

    maskTypeInt : function (event) {
       if ( !(event.charCode >= 48 && event.charCode <= 57 ||
          event.charCode === 0) ) {
          event.preventDefault();
       }
    },
    
    maskTypeFloat : function (event) {
       var value = $("#"+this.id).val(),
             dots = value.search(/\./ig),
             commas = value.search(/,/ig);
       if ( !( event.charCode >= 48 && event.charCode <= 57 ||
          (event.charCode === 46 && dots === -1) ||
          (event.charCode === 44 && dots === -1 && commas === -1) ||
          event.charCode === 0) ) {
          event.preventDefault();
       }
    }
    


    В данный момент реализована валидация для следующих типов данных:
    int,
    float,
    price,
    phone,
    text,
    wordProcessor (для валидации tinyMCE),
    combobox,
    form (тут валидация происходит для нескольких полей, опции для которых передаются в массиве items, срабатывает она при нажатии на кнопку, передается id кнопки).

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