Type in the middle
Put the caret anywhere in a half-filled field and keep typing. The counter is the number of times the caret ended up somewhere other than where you left it.
<div class="demo demo--wide">
<label class="demo-hint" for="cnic">A Pakistani CNIC</label>
<form id="form" action="#" onsubmit="return false">
<input class="demo-input" id="cnic" name="cnic" placeholder="42101-1234567-1" inputmode="numeric">
</form>
<p class="demo-hint">Type a few digits, click into the middle, and carry on. Backspace over a
dash. Paste <code>42101-1234567-1</code> over the lot.</p>
<dl class="demo-kv" id="out"></dl>
<div class="demo-row">
<button class="demo-btn demo-btn--ghost" id="paste">Paste a formatted one</button>
<button class="demo-btn demo-btn--ghost" id="clear">Clear</button>
</div>
<p class="demo-hint">The second row is what an ordinary form submit would send — no JavaScript
of yours involved, because the widget moved the field's <code>name</code> onto a hidden input.</p>
</div>
import { createMaskedInput } from 'https://devix.pk/cdn/oss/masked-input@1.0.0/index.js';
const field = document.getElementById('cnic');
const out = document.getElementById('out');
let jumps = 0;
let caretWas = 0;
const widget = createMaskedInput(field, {
mask: 'cnic',
onChange: () => show(),
});
function show() {
const state = widget.read();
const posted = Object.fromEntries(new FormData(document.getElementById('form')).entries());
out.innerHTML = `
<div><dt>in the field</dt><dd>${state.masked || '—'}</dd></div>
<div><dt>what a form would post</dt><dd>${posted.cnic || '—'}</dd></div>
<div><dt>complete</dt><dd>${state.complete ? 'yes' : `${state.raw.length} of 13`}</dd></div>
<div><dt>caret surprises</dt><dd>${jumps}</dd></div>`;
}
// Watch where the caret actually ends up after each keystroke, rather than
// claiming it stays put.
field.addEventListener('keydown', () => { caretWas = field.selectionStart; });
field.addEventListener('keyup', (event) => {
if (event.key.length !== 1) return;
const now = field.selectionStart;
// One forward for the character, and one more if a separator was crossed.
if (now !== caretWas + 1 && now !== caretWas + 2) jumps += 1;
show();
});
document.getElementById('paste').addEventListener('click', () => {
widget.setValue('42101-1234567-1');
field.focus();
show();
});
document.getElementById('clear').addEventListener('click', () => {
widget.setValue('');
jumps = 0;
field.focus();
show();
});
show();
@import url('https://devix.pk/cdn/oss/masked-input@1.0.0/masked-input.min.css');
#cnic { width: 100%; }
.demo-kv dd { font-variant-numeric: tabular-nums; }