В сценариях реальных интерфейсов статические маски быстро упираются в ограничения: телефонные форматы зависят от страны, паспортные данные — от типа документа, банковские реквизиты — от региона и конкретного API банка. В таких условиях используется связка пользовательского ввода с внешними источниками данных, где маска формируется или модифицируется на лету.
Inputmask предоставляет несколько механизмов, позволяющих строить
такие решения: динамическое определение маски через функцию, изменение
правил через хуки (onBeforeMask,
onBeforePaste, onBeforeWrite), а также
программное обновление конфигурации экземпляра.
Интеграция с API или любым внешним источником обычно строится по следующей схеме:
Ключевая особенность: маска в Inputmask не обязана быть статичной строкой. Она может быть функцией, возвращающей конфигурацию.
Один из наиболее гибких механизмов — использование функции вместо
фиксированного значения mask.
import Inputmask from "inputmask";
let currentCountry = "KZ";
const phoneMask = Inputmask({
mask: function () {
if (currentCountry === "KZ") {
return "+7 (999) 999-99-99";
}
if (currentCountry === "DE") {
return "+49 9999 9999999";
}
return "+9{1,3} 999999999";
}
});
phoneMask.mask(document.querySelector("#phone"));
Маска пересчитывается каждый раз при инициализации или обновлении, что позволяет подменять поведение в зависимости от внешнего контекста.
Типичный сценарий: currentCountry обновляется после
ответа API геолокации или выбора пользователя.
При работе с сервером конфигурация маски может приходить в виде JSON:
{
"country": "KZ",
"mask": "+7 (999) 999-99-99"
}
Интеграция выполняется через асинхронную загрузку:
import Inputmask from "inputmask";
async function initPhoneMask() {
const res = await fetch("/api/phone-mask");
const data = await res.json();
const im = new Inputmask({
mask: data.mask
});
im.mask(document.querySelector("#phone"));
}
initPhoneMask();
В этом случае Inputmask используется как конечный слой, а вся логика определения формата переносится во внешнюю систему.
Частая задача — смена маски после выбора страны или типа документа. Вместо повторной инициализации можно обновлять инстанс:
const im = new Inputmask({ mask: "+7 (999) 999-99-99" });
im.mask(document.querySelector("#phone"));
function updateMask(newMask) {
im.opts.mask = newMask;
im.setValue("");
im.mask(document.querySelector("#phone"));
}
Более корректный подход — полное снятие и повторное применение:
Inputmask.remove(document.querySelector("#phone"));
new Inputmask({ mask: newMask }).mask(document.querySelector("#phone"));
Это предотвращает накопление внутренних состояний.
onBeforeMask часто применяется как слой адаптации
данных, приходящих из API или базы.
Inputmask({
mask: "+7 (999) 999-99-99",
onBeforeMask: function (value) {
// нормализация номера из внешнего источника
return value.replace(/[^\d]/g, "");
}
}).mask("#phone");
Если API возвращает номер в разных форматах (+7,
8, 007), функция позволяет привести данные к
единому виду до применения маски.
Сложный случай — выбор маски по типу документа, полученному из внешнего справочника:
async function getDocTypeMask(typeId) {
const res = await fetch(`/api/doc-types/${typeId}`);
return res.json();
}
async function applyDocMask(typeId) {
const config = await getDocTypeMask(typeId);
const maskInstance = new Inputmask({
mask: config.mask,
placeholder: config.placeholder
});
maskInstance.mask(document.querySelector("#document"));
}
Такой подход позволяет централизовать бизнес-логику в backend, оставляя frontend максимально универсальным.
В Inputmask можно определять пользовательские символы. Это особенно полезно, если правила формата приходят с сервера.
const apiConfig = {
definitions: {
"X": {
validator: "[A-F0-9]",
casing: "upper"
}
},
mask: "XX-9999"
};
Inputmask(apiConfig).mask("#code");
Если definitions загружаются динамически, их можно применять перед инициализацией маски:
async function init() {
const res = await fetch("/api/mask-definitions");
const config = await res.json();
Inputmask({
definitions: config.definitions,
mask: config.mask
}).mask("#field");
}
Часто внешним источником выступает не сервер, а локальные данные пользователя: выбранная страна, язык интерфейса, роль.
function buildMask(userProfile) {
if (userProfile.role === "admin") {
return "9999-AAAA";
}
if (userProfile.region === "EU") {
return "+49 999 999999";
}
return "+7 (999) 999-99-99";
}
const mask = new Inputmask({
mask: buildMask(currentUser)
});
При частых обновлениях внешних данных (например, автодополнение страны по вводу) важно избегать постоянного пересоздания маски.
let timer;
function updateMaskFromQuery(query) {
clearTimeout(timer);
timer = setTimeout(async () => {
const res = await fetch(`/api/country?q=${query}`);
const data = await res.json();
Inputmask.remove("#phone");
new Inputmask({
mask: data.mask
}).mask("#phone");
}, 300);
}
Задержка снижает количество обращений к API и предотвращает «дёргание» интерфейса.
В системах реального времени маска может обновляться через потоковые данные:
const socket = new WebSocket("wss://example.com/mask-stream");
socket.onmess age = (event) => {
const data = JSON.parse(event.data);
Inputmask.remove("#field");
new Inputmask({
mask: data.mask
}).mask("#field");
};
Такой подход используется в финансовых интерфейсах, где формат ввода зависит от выбранного инструмента или региона торговли.
onBeforeWrite позволяет контролировать итоговое значение
перед его фиксацией, что полезно при внешней валидации:
Inputmask({
mask: "+7 (999) 999-99-99",
onBeforeWrite: function (event, buffer, caretPos, opts) {
const raw = buffer.join("");
if (raw.length < 5) {
return false;
}
return buffer;
}
}).mask("#phone");
При интеграции с API это позволяет блокировать некорректные состояния, пришедшие извне.
В сложных приложениях маска формируется из нескольких источников:
Пример композиции:
function resolveMask(system, user, api) {
return api.mask
|| user.customMask
|| system.defaultMask;
}
const mask = new Inputmask({
mask: resolveMask(systemConfig, userProfile, apiResponse)
});
Такой подход обеспечивает предсказуемую иерархию приоритетов.
Внешние системы часто возвращают маски в несовместимом формате. Перед передачей в Inputmask требуется нормализация:
function normalizeMask(mask) {
return mask
.replace(/\d/g, "9")
.replace(/[A-Z]/g, "a");
}
Использование:
const config = await fetch("/api/mask");
new Inputmask({
mask: normalizeMask(config.mask)
}).mask("#input");
При интеграции с API важно учитывать:
Эти ограничения требуют явного управления жизненным циклом маски, особенно при частых обновлениях данных.
Корректная стратегия обновления обычно включает:
async function refreshMask() {
const input = document.querySelector("#phone");
const value = input.value;
Inputmask.remove(input);
const res = await fetch("/api/new-mask");
const config = await res.json();
const im = new Inputmask({ mask: config.mask });
im.mask(input);
input.value = value;
}
Такой подход минимизирует потерю данных и обеспечивает согласованность состояния поля.