Maska
Do you like Maska? Please support me via Boosty.
Live Demo
Here are several examples of basic masks that you could create with Maska. This demo is interactive: feel free to edit the examples.
Install
Via npm
npm i maska
CDN / Global build
You can use Maska directly from CDN with simple script tag.
Library API will be exposed on the global Maska object:
<script src="https://cdn.jsdelivr.net/npm/maska@2/dist/maska.umd.js"></script>
<script>
const { Mask, MaskInput, vMaska } = Maska
new MaskInput("[data-maska]") // for masked input
const mask = new Mask({ mask: "#-#" }) // for programmatic use
Vue.createApp({ directives: { maska: vMaska }}).mount('#app') // Vue directive
</script>
CDN / ES modules
For modern browsers you can use ES modules build with (optional) import maps:
<script type="importmap">
{
"imports": {
"maska": "https://cdn.jsdelivr.net/npm/maska@2/dist/maska.js"
}
}
</script>
<script type="module">
import { Mask, MaskInput, vMaska } from 'maska'
new MaskInput("[data-maska]") // for masked input
const mask = new Mask({ mask: "#-#" }) // for programmatic use
Vue.createApp({ directives: { maska: vMaska }}).mount('#app') // Vue directive
</script>
Notice that we are using <script type="module"> in this case.
Usage
Maska library consists of three main components:
Maskclass to mask string valuesMaskInputclass to applyMaskprocessing to<input>vMaskadirective to simplify using of library within Vue components
Vanilla JS
Start with simple input element and data-maska attribute:
<input data-maska="#-#">
Then import and init MaskInput, passing input element(s) or querySelector expression as first argument:
import { MaskInput } from "maska"
new MaskInput("[data-maska]")
Usually you set mask via data-maska attribute. But you can pass mask and other options via second argument (it will be a default options that can be overriden by data-maska- attributes):
new MaskInput("input", { mask: "#-#" })
To destroy mask use destroy() method:
const mask = new MaskInput(...)
mask.destroy()
Vue
Import vMaska directive and apply it to the input along with data-maska attribite:
Composition API
<script setup>
import { vMaska } from "maska"
</script>
<template>
<input v-maska data-maska="#-#">
</template>
Options API
<script>
import { vMaska } from "maska"
export default {
directives: { maska: vMaska }
}
</script>
<template>
<input v-maska data-maska="#-#">
</template>
Bind value
To get masked value you can use standard v-model directive.
But if you want to access an unmasked (raw) value, you can pass a variable as v-maska directive value.
This variable should be a reactive object that will contains three fields after mask processing:
masked: string with masked resultunmasked: string with unmasked resultcompleted: boolean flag indicating that mask is completed
Composition API
<script setup>
import { reactive, ref } from "vue"
import { vMaska } from "maska"
const maskedValue = ref('')
const boundObject = reactive({})
</script>
<template>
<input v-maska="boundObject" v-model="maskedValue">
Masked value: {{ maskedValue }} or {{ boundObject.masked }}
Unmasked value: {{ boundObject.unmasked }}
<span v-if="boundObject.completed">✅ Mask completed</span>
</template>
Options API
<script>
import { vMaska } from "maska"
export default {
directives: { maska: vMaska },
data: () => ({
maskedValue: "",
boundObject: {
masked: "",
unmasked: "",
completed: false
}
})
}
</script>
<template>
<input v-maska="boundObject" v-model="maskedValue">
Masked value: {{ maskedValue }} or {{ boundObject.masked }}
Unmasked value: {{ boundObject.unmasked }}
<span v-if="boundObject.completed">✅ Mask completed</span>
</template>
Set mask options
To set default options for the mask you could use directive argument (part after v-maska:). It can be plain or reactive object and should be wrapped in []:
Composition API
<script setup>
import { reactive } from "vue"
import { vMaska } from "maska"
const options = reactive({
mask: "#-#",
eager: true
})
</script>
<template>
<input v-maska:[options]>
</template>
Options API
<script>
import { vMaska } from "maska"
export default {
directives: { maska: vMaska },
data: () => ({
options: {
mask: "#-#",
eager: true
}
})
}
</script>
<template>
<input v-maska:[options]>
</template>
?> Please see issue#149: options object should be assigned in the current file.
You can set options and bind to an object at the same time:
<input v-maska:[options]="boundObject">
Global registration of directive
Vue 3
import { createApp } from "vue"
import { vMaska } from "maska"
createApp({}).directive("maska", vMaska)
// or in case of CDN load
Vue.createApp({}).directive("maska", Maska.vMaska)
Vue 2
import Vue from "vue"
import { vMaska } from "maska"
Vue.directive("maska", vMaska)
// or in case of CDN load
Vue.directive("maska", Maska.vMaska)
Nuxt 3
To use Maska in Nuxt 3 you can make a simple plugin. Create file maska.ts in plugins folder:
import { vMaska } from "maska"
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive("maska", vMaska)
})
Now you can use v-maska directive in your app:
<input v-model="value" v-maska data-maska="#-#" />
Options
Mask options
Every option of Mask class can be set via data-maska- attributes or by passing on init.
Options passed on init will be used as defaults and could be overriden by data-maska- attributes.
Description
mask / data-maska— mask to apply (string, array of strings or function). If you pass empty string ornullit will disable a masktokens / data-maska-tokens— custom tokens objecttokensReplace / data-maska-tokens-replace— if custom tokens should replace default tokens (if not set, they will merge)eager / data-maska-eager— eager mode will show static characters before you type them, e.g. when you type1, eager mask#-#will show1-and non-eager will show1reversed / data-maska-reversed— in reversed mode mask will grow backwards, e.g. for numbers
Types
interface MaskOptions {
mask?: MaskType
tokens?: MaskTokens
tokensReplace?: boolean
eager?: boolean
reversed?: boolean
}
<input data-maska="A-A" data-maska-tokens="A:[A-Z]" data-maska-eager>
MaskInput options
MaskInput options could be set only by passing second argument on init.
Along with MaskInput options you could pass any Mask options as well (or set them via data-maska- attributes).
Description
onMaska— callback on every mask processingpreProcess— hook before mask processingpostProcess— hook after mask processing
Types
interface MaskInputOptions extends MaskOptions {
onMaska?: (detail: MaskaDetail) => void
preProcess?: (value: string) => string
postProcess?: (value: string) => string
}
new MaskInput("input", {
mask: "#-#",
reversed: true,
onMaska: (detail: MaskaDetail) => console.log(detail.completed)
})
Tokens
There are 3 tokens defined by default:
{
'#': { pattern: /[0-9]/ }, // digits
'@': { pattern: /[a-zA-Z]/ }, // letters
'*': { pattern: /[a-zA-Z0-9]/ }, // letters & digits
}
?> Use ! before token to escape symbol. For example !# will render # instead of a digit.
Custom tokens
Add custom tokens via data-maska-tokens attribute or by tokens option:
By data-attr
When using data-maska-tokens, there are two possible formats:
- Full form should be a valid JSON-string (but can use both single and double quotes) with pattern in string format without delimiters
- Simple form should be a string in format:
T:P:M|T:P:Mwhere:Tis tokenPis pattern in string formMis optional modifier (see below)|is separator for multiple tokens
<input data-maska="Z-Z" data-maska-tokens="{ 'Z': { 'pattern': '[A-Z]' }}">
<input data-maska="Z-Z" data-maska-tokens="Z:[A-Z]">
<input data-maska="Z-z" data-maska-tokens="Z:[A-Z]|z:[a-z]:multiple">
?> You can’t set transform function for token via data-maska-tokens.
If you need this, use tokens option instead.
By option
When using tokens option, pattern should be a valid regular expression object:
new MaskInput("[data-maska]", {
mask: "A-A",
tokens: {
A: { pattern: /[A-Z]/, transform: (chr: string) => chr.toUpperCase() }
}
})
Token modifiers
Every token can have a modifier, for example:
{
0: { pattern: /[0-9]/, optional: true },
9: { pattern: /[0-9]/, repeated: true },
}
optionalfor optional tokenmultiplefor token that can match multiple characters till the next token startsrepeatedfor tokens that should be repeated. This token will match only one character, but the token itself (or group of such tokens) can be repeated any number of times
| Modifier | Example usage | Mask | Tokens |
|---|---|---|---|
optional |
IP address | #00.#00.#00.#00 |
0:[0-9]:optional |
multiple |
CARDHOLDER NAME | A A |
A:[A-Z]:multiple |
repeated |
Money (1 234,56) | 9 99#,## reversed |
9:[0-9]:repeated |
Transform tokens
For custom tokens you can define transform function, applied to a character before masking.
For example, transforming letters to uppercase on input:
{
A: { pattern: /[A-Z]/, transform: (chr: string) => chr.toUpperCase() }
}
?> You can also use hooks for transforming whole value before/after masking.
Dynamic masks
Pass mask as array or function to make it dynamic:
- With array a suitable mask will be chosen by length (smallest first)
- With function you can select mask with custom logic using value
new MaskInput("input", {
mask: (value: string) => value.startsWith('1') ? '#-#' : '##-##'
})
Hooks
Use hooks for transforming whole value:
preProcesshook called before mask processingpostProcesshook called after mask processing, but before setting input's value
For example, you can use it to check for a maximum length of masked string:
new MaskInput("input", {
postProcess: (value: string) => value.slice(0, 5)
})
Events
There are two events you can subscribe to get the masking result:
maskaeventinputevent
They are essentially the same, but the input event could be fired by any input logic, and the maska event is library specific.
Vanilla JS
document.querySelector("input").addEventListener("maska", onMaska)
Vue
<input v-maska data-maska="#-#" @maska="onMaska" />
Both events contains detail property with given structure:
Description
masked: masked valueunmasked: unmasked valuecompleted: flag that current mask is completed
Types
interface MaskaDetail {
masked: string
unmasked: string
completed: boolean
}
const onMaska = (event: CustomEvent<MaskaDetail>) => {
console.log({
masked: event.detail.masked,
unmasked: event.detail.unmasked,
completed: event.detail.completed
})
}
Alternatively, you can pass callback function directly with MaskInput option onMaska:
Vanilla JS
new MaskInput("input", {
onMaska: (detail: MaskaDetail) => console.log(detail.completed)
})
Vue
<script setup>
const options = {
onMaska: (detail: MaskaDetail) => console.log(detail.completed)
}
</script>
<template>
<input v-maska:[options]>
</template>
Programmatic use
You can use mask function programmatically by importing Mask class.
There are three methods available:
masked(value)returns masked version of given valueunmasked(value)returns unmasked version of given valuecompleted(value)returnstrueif given value makes mask complete
import { Mask } from "maska"
const mask = new Mask({ mask: "#-#" })
mask.masked("12") // = 1-2
mask.unmasked("12") // = 12
mask.completed("12") // = true
Known issues
When used on input of type number, could have inconsistent behavior in different browsers. Use attribute inputmode="numeric" with type="text" if you need a numeric keyboard.