Skip to content

Input field ​

attach puts a mask on an <input>. The hard part of masks isn't formatting but editing behaviour, and attach takes care of it.

ts
import { attach } from 'tinyval-mask';
import { phone } from 'tinyval-mask/presets';

const input = document.querySelector<HTMLInputElement>('#phone')!;
const field = attach(input, phone(), {
  onChange: ({ value, raw, complete }) => console.log(value, raw, complete),
});

field.raw;      // '79991234567'
field.complete; // true
field.destroy(); // remove the mask

What happens while typing ​

ActionBehaviour
Typing at the endLiterals appear by themselves: 9 → +7 (9
Typing in the middleCharacters shift, the caret stays right after the typed one
Backspace after ) or -The nearest digit is removed; the caret doesn't get stuck on the literal
Delete before a literalThe same in the other direction
PasteAny format is accepted: 8 999 123-45-67, +79991234567, (999) 1234567
Unsuitable charactersIgnored: letters won't appear in a phone number

The caret is tracked by typed characters, not by position in the string. So it doesn't jump even when the mask adds or removes separators — e.g. thousands spaces in numberMask.

Framework compatibility ​

attach formats the value before framework handlers (an input listener in the capture phase), so v-model, React's onChange and similar get the formatted value. After a programmatic edit (Backspace through a literal) attach dispatches an input event itself.

If the field value is changed from code (input.value = …), call field.update(mask) — it reformats the value.

See also ​