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>
For modern browsers es201X builds available (imask.es.js
and imask.es.min.js
).
Simple use case:
var element = document.getElementById('selector');
var maskOptions = {
mask: '+{7}(000)000-00-00'
};
var mask = new IMask(element, maskOptions);
Since v1.0.0 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 = new 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);
Input Mask Common Behavior
IMask.Masked is a base class of all other Masked* classes. Almost all base class options are applicable to subclasses.
Example usage:
var digitsMask = new 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:
var caseMask = new 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: don't change Masked
instance inside callbacks.
Example of using Function
mask to accept any growing sequence from 0 to 9:
var sequenceMask = new IMask(element, {
mask: function (value) {
return /^\d*$/.test(value) &&
value.split('').every(function(ch, i) {
var prevCh = value[i-1];
return !prevCh || prevCh < ch;
});
}
});
Number Mask
Number mask restricts input to integer or decimal numbers in many ways:
var numberMask = new 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:
var patternMask = new IMask(element, {
mask: '{#}000[aaa]/NIC-`*[**]'
});
// or without UI element
var masked = new IMask.PatternMasked({
mask: '{#}000[aaa]/NIC-`*[**]'
});
where definitions are:
0
- any digita
- 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
:
var zipMask = new 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:
var phoneMask = new 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:
var blocksMask = new 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']
}
}
});
Range Mask
Range mask extends Pattern mask and can be used to restrict input in a number range.
var rangeMask = new IMask(element, {
mask: IMask.MaskedRange,
from: 1,
to: 99,
// maxLength is optional parameter to set the length of mask. To input smaller values pad zeros at start
maxLength: 3,
// 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.
Enum Mask
Enum mask extends Pattern mask and can be used to restrict input within characters enum.
var enumMask = new 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.
var dateMask = new 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
// also Pattern options can be set
lazy: false
});
It is easy to use it with Moment.js:
var momentFormat = 'YYYY/MM/DD HH:mm';
var momentMask = new 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.
var dynamicMask = new 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:
var dispatchMask = new 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;
});
}
}
)