Хуки и middleware

Архитектурная модель перехвата ввода

Inputmask строится вокруг идеи последовательной обработки пользовательского ввода через цепочку преобразований. Любое значение проходит несколько стадий: захват события, предварительная нормализация, применение маски, валидация, финальная запись в input и обратное преобразование при чтении значения.

В этой цепочке используются хуки (hooks) — точки перехвата, позволяющие вмешиваться в поток данных до или после ключевых операций. В более широком смысле эти механизмы образуют слой middleware-подобной архитектуры, где каждый обработчик может модифицировать входные данные или поведение маски.


Основные типы хуков

onBeforeMask

Хук вызывается перед применением маски к исходному значению (например, при инициализации или программной установке значения).

Используется для:

  • предварительной нормализации данных
  • очистки входных значений
  • преобразования форматов (например, даты или телефоны из разных источников)

Пример:

Inputmask({
  mask: "+7 (999) 999-99-99",
  onBeforeMask: function (value) {
    return value.replace(/[^\d]/g, "");
  }
}).mask(document.querySelector("input"));

Поведение:

  • вход: сырые данные
  • выход: нормализованная строка, готовая к маскированию

onBeforePaste

Хук срабатывает при вставке данных из буфера обмена до обработки маской.

Основные задачи:

  • фильтрация мусорных символов
  • адаптация внешних форматов (международные номера, даты)
  • предотвращение некорректных вставок
Inputmask({
  mask: "9999-9999-9999-9999",
  onBeforePaste: function (pastedValue) {
    return pastedValue.replace(/\s+/g, "");
  }
}).mask(input);

Особенность:

  • влияет только на данные, приходящие через paste-событие
  • не затрагивает программные установки значения

onBeforeWrite

Один из ключевых middleware-хуков, вызывается перед записью значения в input после обработки маской.

Позволяет:

  • модифицировать финальный результат
  • блокировать обновление значения
  • внедрять бизнес-логику форматирования
Inputmask({
  mask: "999-99-9999",
  onBeforeWrite: function (event, buffer, caretPos, opts) {
    if (buffer.join("").startsWith("000")) {
      return false; // блокировка записи
    }
  }
}).mask(input);

Сигнатура обычно включает:

  • текущее событие
  • буфер маски
  • позицию курсора
  • опции инстанса

onBeforeValidate

Используется в промежуточной стадии валидации символов. Позволяет вмешаться до применения правил definitions.

Основные сценарии:

  • динамическое разрешение символов
  • контекстная валидация (зависимость от позиции)
  • переопределение поведения дефиниций
Inputmask({
  mask: "a{1,3}-999",
  onBeforeValidate: function (chars, buffer, position) {
    return chars.toLowerCase();
  }
});

Middleware-логика внутри Inputmask

Принцип цепочки обработки

Каждый ввод проходит через серию преобразователей:

  1. Захват события (keydown, input, paste)
  2. Предварительный фильтр (onBeforePaste, onBeforeMask)
  3. Применение маски
  4. Символьная валидация (definitions)
  5. Постобработка (onBeforeWrite)
  6. Запись в DOM
  7. Синхронизация состояния маски

Эта структура фактически реализует pipeline middleware, где каждый слой может:

  • модифицировать данные
  • прервать выполнение
  • вернуть изменённый результат

Расширение через definitions как middleware

Помимо хуков, Inputmask предоставляет механизм definitions, который можно рассматривать как декларативный middleware для символов маски.

Inputmask({
  mask: "X-999",
  definitions: {
    "X": {
      validator: "[A-F]",
      casing: "upper"
    }
  }
}).mask(input);

Здесь каждый символ проходит обработчик:

  • проверка валидности
  • преобразование регистра
  • сопоставление с шаблоном

Постобработка через событийные хуки

Inputmask также предоставляет события, которые дополняют middleware-цепочку:

  • oncomplete — маска заполнена полностью
  • onincomplete — маска не завершена
  • oncleared — значение очищено
  • onKeyValidation — результат проверки клавиши

Пример:

Inputmask({
  mask: "999-999",
  oncomplete: function () {
    console.log("Маска заполнена");
  },
  onincomplete: function () {
    console.log("Маска не завершена");
  }
}).mask(input);

Эти события не изменяют данные напрямую, но расширяют контроль над состоянием маски.


Контекст выполнения хуков

Состояние маски

Каждый хук получает доступ к внутреннему состоянию:

  • буфер символов
  • текущая позиция курсора
  • конфигурация маски
  • режим вставки или ввода

Это позволяет строить сложные сценарии, где поведение зависит не только от входных данных, но и от текущего состояния поля.


Приоритет обработки

При конфликте нескольких модификаций действует правило последовательности:

onBeforePaste → onBeforeMask → definitions → onBeforeValidate → onBeforeWrite

Любое вмешательство на раннем этапе сильнее влияет на итоговый результат, чем постобработка.


Динамические middleware-паттерны

Переключение масок через хуки

Хуки часто используются для динамического изменения маски в зависимости от вводимых данных.

Inputmask({
  mask: function (opts) {
    return opts.phoneType === "international"
      ? "+99 (999) 999-9999"
      : "(999) 999-9999";
  },
  onBeforeMask: function (value) {
    return value;
  }
}).mask(input);

Здесь middleware-логика определяет структуру маски на лету.


Композиция обработчиков

Несколько хуков могут работать совместно, формируя цепочку преобразований:

Inputmask({
  mask: "9999 9999",
  onBeforePaste: v => v.replace(/\D/g, ""),
  onBeforeMask: v => v.slice(0, 8),
  onBeforeWrite: (e, buffer) => buffer
}).mask(input);

Каждый этап выполняет свою узкую роль:

  • очистка
  • ограничение длины
  • финальная стабилизация вывода

Взаимодействие с пользовательскими событиями DOM

Хуки Inputmask часто комбинируются с нативными событиями браузера:

  • input
  • change
  • keydown
  • blur

При этом важно учитывать, что Inputmask перехватывает и переопределяет часть стандартного поведения ввода, создавая собственный слой абстракции над DOM-событиями.

Middleware-хуки работают внутри этого слоя, а не поверх него, что делает их более предсказуемыми по сравнению с внешними обработчиками событий.


Ограничения и особенности поведения

  • Хуки не предназначены для асинхронной логики; возврат Promise не поддерживается
  • Изменение значения должно быть синхронным
  • Некоторые хуки могут не вызываться при программном setValue, если не активирован пересчёт маски
  • В сложных масках с jitMasking поведение onBeforeWrite может отличаться из-за ленивой генерации символов

Комбинирование с alias и extend-паттернами

Inputmask позволяет расширять базовые маски через alias, где middleware-логика может быть встроена в переиспользуемые шаблоны.

Inputmask.extendAliases({
  "custom-phone": {
    mask: "+7 (999) 999-99-99",
    onBeforePaste: function (v) {
      return v.replace(/[^\d]/g, "");
    }
  }
});

Такой подход превращает хуки в часть декларативной конфигурации, а не локальной логики.


Поведение при программном изменении значения

При вызове:

inputmask.setValue("123456789");

последовательность обработки повторяет middleware pipeline:

  • значение проходит onBeforeMask
  • применяется маска
  • выполняется валидация
  • вызывается onBeforeWrite

Это обеспечивает единообразие между пользовательским вводом и программными изменениями.