--- layout: default title: Input Mask Guide --- {% include guide_header.html %}

## Getting Started

Install from npm:

    npm install imask

And import or require:

    import IMask from 'imask';

or use CDN:

    <script src="https://unpkg.com/imask"></script>

Simple use case:

demo
Type here against mask +{7}(000)000-00-00
unmasked:

    var element = document.getElementById('selector');
    var maskOptions = {
      mask: '+{7}(000)000-00-00'
    };
    var mask = IMask(element, maskOptions);

Please note

If you apply mask to `input` element you have to use `type="text"`. Other types are not supported.

IMask consists of two independent layers: model and view.
Model layer contains all masking facilities which can be used independently without UI.

View layer is a glue between UI element and model, it connects listeners and controls changes in both directions.

Input processing is based on a simple idea of comparing states before and after change. State before change is obtained on `keydown` and on `input` actual processing takes place. In order to support older browsers manually call `_saveSelection` to save state and `_onInput` to handle changes. Pull requests for the beauty are welcomed.

Currently, view layer contains only one component `InputMask` which supports HTML input-like API. Instance of `InputMask` is returned when IMask constructor is called.

To create new mask on element use:

    var mask = IMask(element, maskOptions);

Get/set value and unmasked value:

    mask.value = "+7(999)999-99-99";
    console.log(mask.value);  // "+7(999)999-99-99"
    console.log(mask.unmaskedValue);  // "79999999999"

    mask.unmaskedValue = "70000000000";
    console.log(mask.value);  // "+7(000)000-00-00"
    console.log(mask.unmaskedValue);  // "70000000000"

For typed masks like `Number` or `Date` it is possible to work with typed values:

    mask.updateOptions({mask: Number});
    mask.typedValue = 100;  // use number
    console.log(mask.value);  // "100"
    console.log(mask.unmaskedValue);  // "100"
    console.log(mask.typedValue);  // 100

For untyped masks `typedValue` and `unmaskedValue` works identically.

Update options:

    mask.updateOptions({
      mask: Number,
      min: 0,
      max: 100
    });  // also updates UI

Clean and destroy:

    mask.destroy();

Listen to events:

    // 'accept' event fired on input when mask value has changed
    function log () {console.log(mask.value)};
    mask.on("accept", log);

    // 'complete' event fired when the value is completely filled
    // Note: this makes sense only for Pattern-based masks
    mask.on("complete", function () {console.log(mask.value)});

Stop listening to events:

    mask.off("accept", log);

    // omit handler argument to unbind all handlers
    mask.off("complete");

Get `Masked` model:

    var masked = mask.masked;
    masked.reset(); // UI will NOT be updated

In the above example all changes are proxied to the model layer first and then UI is updated. The core of masking on model layer is IMask.Masked base class. There are also several other model classes for the different `mask` property types that provide additional functionality:

`mask` prop | Model class
--- | ---
`IMask.Masked` descendant or instance | IMask.Masked
`RegExp` | IMask.MaskedRegExp
`Function` | IMask.MaskedFunction
`String` | IMask.MaskedPattern
`Number` | IMask.MaskedNumber
`Date` | IMask.MaskedDate
`Array` of masks | IMask.MaskedDynamic

Mask also can be used without UI, e.g.

    var masked = IMask.createMask({
      mask: '+7 (000) 000-00-00',
      // ...and other options
    });
    var maskedValue = masked.resolve('71234567890');

    // mask keeps state after resolving
    console.log(masked.value);  // same as maskedValue
    // get unmasked value
    console.log(masked.unmaskedValue);

## Masked

### Common Behavior

IMask.Masked is a base class of all other `Masked*` classes. Almost all base class options are applicable to subclasses.

Example usage:

demo
Type here against RegExp mask /^\d+$/

    var digitsMask = IMask(element, {
      mask: /^\d+$/
    });

Update options:

    mask.updateOptions({
      // while changing mask only same type allowed
      mask: /^\w+$/,  // ok
      // mask: "0000",  // ERROR! changing mask type on existing mask is not allowed!

      // ... other options
    });

Get/set value and unmasked value:

    masked.value = 'hello world!';
    console.log(masked.unmaskedValue);
    // or typed value if it makes sense
    console.log(masked.typedValue);

Use `prepare (value, masked)` option for preprocessing input and `commit (value, masked)` option for postprocessing after UI is deactivated:

demo
Type to uppercase

    var caseMask = IMask(element, {
      mask: /^\w+$/,
      prepare: function (str) {
        return str.toUpperCase();
      },
      commit: function (value, masked) {
        // Don't change value manually! All changes should be done in mask!
        // This example helps to understand what is really changes, only for demo
        masked._value = value.toLowerCase();  // Don't do it
      }
    });

Usually you don't need to create instances of that type manually, because it will be done by IMask internally. But you can subclass from `Masked` to add some specific behavior.

Additionaly to `mask` option custom validator can be set as `validate (value, masked)` option for some complex checks on any mask types excluding `Function` and `RegExp`, because they are already validators themselves.

Note: do not mutate `Masked` instance inside callbacks.

Also make sure that mask or validator works with any of intermediate states, not just final value. For example to restrict input to "123" you do:

    var masked = IMask.Masked({
      mask: /^123$/
    });

But it does not allow to input any symbol at all, because it matches only whole string "123" and not "1" nor "12". Always take care of intermediate values at first, otherwise it might not work as expected. In complex cases it is better to use `Pattern` or `Function` masks.

Example of using `Function` mask to accept any growing sequence from 0 to 9:

demo
Type here

    var sequenceMask = IMask(element, {
      mask: function (value) {
        return /^\d*$/.test(value) &&
          value.split('').every(function(ch, i) {
            var prevCh = value[i-1];
            return !prevCh || prevCh < ch;
          });
      }
    });

#### Overwrite Mode `since 5.0`

Enables characters overwriting (replace or shift) instead of inserting.

Simple `6-digits` mask in overwrite mode
source

    var overwriteMask = IMask(
      document.getElementById('overwrite-mask'),
      {
        mask: '000000',
        lazy: false,
      }
    );

### Number Mask

Number mask restricts input to integer or decimal numbers in many ways:

demo
Type number here
number:
Options
Scale
Radix char
Map to radix
Signed Pad fractional zeros Normalize zeros
Min
Max
Thousands separator

    var numberMask = IMask(element, {
      mask: Number,  // enable number mask

      // other options are optional with defaults below
      scale: 2,  // digits after point, 0 for integers
      signed: false,  // disallow negative
      thousandsSeparator: '',  // any single char
      padFractionalZeros: false,  // if true, then pads zeros at end to the length of scale
      normalizeZeros: true,  // appends or removes zeros at ends
      radix: ',',  // fractional delimiter
      mapToRadix: ['.'],  // symbols to process as radix

      // additional number interval options (e.g.)
      min: -10000,
      max: 10000
    });

### Pattern Mask

Use pattern when:

- mask is complex or contains nested masks
- mask is fixed in size (optional symbols can provide some flexibility)
- placeholder is needed
- more reliability or flexibility on processing input is needed

Pattern mask is just a string:

demo
Type here
unmasked:
Options
Mask
Placeholder char
lazy placeholder overwrite mode eager mode
Values
Set raw value
or
Set unmasked value

    var patternMask = IMask(element, {
      mask: '{#}000[aaa]/NIC-`*[**]'
    });

    // or without UI element
    var masked = IMask.PatternMasked({
      mask: '{#}000[aaa]/NIC-`*[**]'
    });

where definitions are:

- `0` - any digit
- `a` - any letter
- `*` - any char
- other chars which is not in custom definitions supposed to be fixed
- `[]` - make input optional
- `{}` - include fixed part in unmasked value
- ``` - prevent symbols shift back

If definition character should be treated as fixed it should be escaped by `\\` (E.g. `\\0`).

Additionally you can provide custom `definitions`:

demo
Type here against mask #00000
unmasked:

    var zipMask = IMask(element, {
      mask: '#00000',
      definitions: {
        // <any single char>: <same type as mask (RegExp, Function, etc.)>
        // defaults are '0', 'a', '*'
        '#': /[1-6]/
      }
    });

To configure placeholder use:

demo
Type here against mask +{7}(000)000-00-00 with custom placeholder
unmasked:

    var phoneMask = IMask(element, {
      mask: '+{7}(000)000-00-00',
      lazy: false,  // make placeholder always visible
      placeholderChar: '#'     // defaults to '_'
    });

For complex nested masks there is `blocks` option available:

demo
Type here against mask
unmasked:

    var blocksMask = IMask(element, {
      mask: 'Ple\\ase fill ye\\ar 19YY, month MM \\and v\\alue VL',
      lazy: false,  // make placeholder always visible

      blocks: {
        YY: {
          mask: '00',
        },

        MM: {
          mask: IMask.MaskedRange,
          from: 1,
          to: 12
        },

        VL: {
          mask: IMask.MaskedEnum,
          enum: ['TV', 'HD', 'VR']
        }
      }
    });

#### Lazy Mode

lazy
source

    var lazyMask = IMask(
      document.getElementById('pattern-lazy-mask'),
      {
        mask: Date,
        lazy: <CHECK>,
        autofix: true,
        blocks: {
          d: {mask: IMask.MaskedRange, placeholderChar: 'd', from: 1, to: 31, maxLength: 2},
          m: {mask: IMask.MaskedRange, placeholderChar: 'm', from: 1, to: 12, maxLength: 2},
          Y: {mask: IMask.MaskedRange, placeholderChar: 'y', from: 1900, to: 2999, maxLength: 4}
        }
      }
    );

#### Eager Mode `since 6.3.0`

eager
source

    var eagerMask = IMask(
      document.getElementById('pattern-eager-mask'),
      {
        mask: '00##00##',
        eager: <CHECK>,
      }
    );

### Range Mask

Range mask extends Pattern mask and can be used to restrict input in a number range.

demo
Type here
value:

    var rangeMask = IMask(element, {
      mask: IMask.MaskedRange,
      from: 1,
      to: 90,
      // maxLength is optional parameter to set the length of mask. To input smaller values pad zeros at start
      maxLength: 3,
      autofix: true,  // bound value

      // also Pattern options can be set
      lazy: false
    });

Unlike Number mask Range mask is fixed in size, accepts only integer values but can use placeholder.

#### Autofix Mode

Input accepts numbers from 0 to 15. Try to type (e.g. `6`) with different autofix modes
source

    var autofixMask = IMask(
      document.getElementById('range-autofix-mask'),
      {
        mask: IMask.MaskedRange,
        from: 0,
        to: 15,
        autofix: <SELECT>,
      }
    );

### Enum Mask

Enum mask extends Pattern mask and can be used to restrict input within characters enum.

demo
Type here
value:

    var enumMask = IMask(element, {
      mask: IMask.MaskedEnum,
      // all values should be same length!
      enum: ['F1', 'G2', 'H3'],

      // also Pattern options can be set
      lazy: false
    });

### Date Mask

Date mask extends Pattern mask with more options.

Please note

if you set `pattern` option, then you also have to provide `format` and `parse` options.

Also Date mask uses independent pattern blocks so it's possible to input inexistent dates until mask is complete. When last character is inserted then input is converted to Date object and get verified. This leads to weird situations when you might have incorrect day, but not being able to insert year. It is not possible to validate Date intermediate states in general way, but you still can use `validate` callback on application side to check your specific case.

demo
Type here
date:

    var dateMask = IMask(element, {
      mask: Date,  // enable date mask

      // other options are optional
      pattern: 'Y-`m-`d',  // Pattern mask with defined blocks, default is 'd{.}`m{.}`Y'
      // you can provide your own blocks definitions, default blocks for date mask are:
      blocks: {
        d: {
          mask: IMask.MaskedRange,
          from: 1,
          to: 31,
          maxLength: 2,
        },
        m: {
          mask: IMask.MaskedRange,
          from: 1,
          to: 12,
          maxLength: 2,
        },
        Y: {
          mask: IMask.MaskedRange,
          from: 1900,
          to: 9999,
        }
      },
      // define date -> str convertion
      format: function (date) {
        var day = date.getDate();
        var month = date.getMonth() + 1;
        var year = date.getFullYear();

        if (day < 10) day = "0" + day;
        if (month < 10) month = "0" + month;

        return [year, month, day].join('-');
      },
      // define str -> date convertion
      parse: function (str) {
        var yearMonthDay = str.split('-');
        return new Date(yearMonthDay[0], yearMonthDay[1] - 1, yearMonthDay[2]);
      },

      // optional interval options
      min: new Date(2000, 0, 1),  // defaults to `1900-01-01`
      max: new Date(2020, 0, 1),  // defaults to `9999-01-01`

      autofix: true,  // defaults to `false`, see details

      // also Pattern options can be set
      lazy: false,

      // and other common options
      overwrite: true  // defaults to `false`
    });

It is easy to use it with Moment.js:

demo
Type here
date:

    var momentFormat = 'YYYY/MM/DD HH:mm';
    var momentMask = IMask(element, {
      mask: Date,
      pattern: momentFormat,
      lazy: false,
      min: new Date(1970, 0, 1),
      max: new Date(2030, 0, 1),

      format: function (date) {
        return moment(date).format(momentFormat);
      },
      parse: function (str) {
        return moment(str, momentFormat);
      },

      blocks: {
        YYYY: {
          mask: IMask.MaskedRange,
          from: 1970,
          to: 2030
        },
        MM: {
          mask: IMask.MaskedRange,
          from: 1,
          to: 12
        },
        DD: {
          mask: IMask.MaskedRange,
          from: 1,
          to: 31
        },
        HH: {
          mask: IMask.MaskedRange,
          from: 0,
          to: 23
        },
        mm: {
          mask: IMask.MaskedRange,
          from: 0,
          to: 59
        }
      }
    });

### Dynamic Mask

Dynamic mask automatically selects appropriate mask from provided array of masks. Mask with the largest number of fitting characters is selected considering provided masks order.

demo
Type color here #HEX or RRR,GGG,BBB

    var dynamicMask = IMask(element, {
      mask: [
        {
          mask: 'RGB,RGB,RGB',
          blocks: {
            RGB: {
              mask: IMask.MaskedRange,
              from: 0,
              to: 255
            }
          }
        },
        {
          mask: /^#[0-9a-f]{0,6}$/i
        }
      ]
    });

It is also possible to select mask manually via `dispatch` option:

demo
Type phone number here Greece, India, Russia codes are recognized
country:

    var dispatchMask = IMask(element, {
        mask: [
          {
            mask: '+00 {21} 0 000 0000',
            startsWith: '30',
            lazy: false,
            country: 'Greece'
          },
          {
            mask: '+0 000 000-00-00',
            startsWith: '7',
            lazy: false,
            country: 'Russia'
          },
          {
            mask: '+00-0000-000000',
            startsWith: '91',
            lazy: false,
            country: 'India'
          },
          {
            mask: '0000000000000',
            startsWith: '',
            country: 'unknown'
          }
        ],
        dispatch: function (appended, dynamicMasked) {
          var number = (dynamicMasked.value + appended).replace(/\D/g,'');

          return dynamicMasked.compiledMasks.find(function (m) {
            return number.indexOf(m.startsWith) === 0;
          });
        }
      }
    )

## Pipe `since 6.0`

Since v6.0.0 `IMask` can be used for formatting or converting values through mask with `pipe` and `createPipe` routines:

    var numberPipe = IMask.createPipe({
      mask: Number,
      scale: 2,
      thousandsSeparator: ' ',
      normalizeZeros: true,
      padFractionalZeros: true,
    });
    // `numberPipe` is just a function, call it to format values
    numberPipe('1'); // "1,00"

    // if `numberPipe` will not be reused, then just use `IMask.pipe` inplace:
    IMask.pipe('1', {
      mask: Number,
      scale: 2,
      thousandsSeparator: ' ',
      normalizeZeros: true,
      padFractionalZeros: true,
    }); // "1,00"

By default `pipe` expects masked value on input and produces masked input as well. This behavior is exactly the same as if a value was pasted in input.

Pipe also provides a way to customize source and destination types:

    // pipe(value, mask|masked, sourcetype, destinationtype);
    // PIPE_TYPE = {TYPED, MASKED, UNMASKED}
    // converts formated string to number
    IMask.pipe(
      '1,01',
      {
        mask: Number,
        scale: 2,
        thousandsSeparator: ' ',
        normalizeZeros: true,
        padFractionalZeros: true,
      },
      IMask.PIPE_TYPE.MASKED,
      IMask.PIPE_TYPE.TYPED
    ); // 1.01 of number type

Note: it is possible to share single `Masked` instance both for input handling and formatting with `pipe`. Just pass `Masked` to `pipe` instead of mask options. In that case `pipe` will not create or clone `Masked` instance, `Masked` state also will not be changed during formatting.

Angular plugin provides `IMaskPipe` for convinience.

## Advanced

### Treeshaking `since 6.0`

`IMask` contains a lot of cool features, which makes it pretty big in size. But you might not need all features and want to reduce your bundle size. Since v6.0.0 `IMask` provides such possibility and ships chunks along side with assembled bundle. Despite mask has been already support esm modules treeshaking did not work on it because of recursive masks and therefore circular dependencies inside. So now you can import only features that you want, for instance if only number mask is used:

    // was before
    // import IMask from 'imask'; // imports all modules

    // enable treeshaking
    import IMask from 'imask/esm/imask'; // imports only factory
    // add needed features
    import 'imask/esm/masked/number';
    // now factory can work with number masks, but not any other

    // usage is same in both cases
    var numberMask = IMask(element, { mask: Number });