Конфликты с другими библиотеками

Библиотека Inputmask активно вмешивается в обработку пользовательского ввода: перехватывает события клавиатуры, изменяет значение поля, управляет позицией курсора, синхронизирует внутренний буфер символов. Именно поэтому она часто вступает в конфликт с другими решениями, работающими с <input>-элементами.

Наиболее распространённые категории конфликтов:

  • UI-фреймворки
  • библиотеки валидации
  • реактивные системы
  • плагины форматирования
  • компоненты автозаполнения
  • виртуальный DOM
  • системы управления состоянием
  • мобильные адаптеры ввода
  • кастомные обработчики событий

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

  • изменяют value
  • отслеживают input
  • управляют caret-позицией
  • предотвращают стандартное поведение браузера
  • переинициализируют DOM
  • синхронизируют состояние поля

Конфликты с jQuery-плагинами

Повторная инициализация элемента

Многие jQuery-плагины клонируют DOM-элементы либо полностью заменяют <input> новым узлом. После этого маска продолжает ссылаться на старый элемент.

Пример проблемы:

$("#phone").inputmask("+7 (999) 999-99-99");

$("#phone").datepicker();

Некоторые плагины создают новый input внутри контейнера, из-за чего:

  • маска перестаёт работать
  • события теряются
  • caret начинает прыгать
  • появляются дубли обработчиков

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

$("#phone").datepicker();

$("#phone").inputmask("+7 (999) 999-99-99");

Конфликт обработчиков input

Многие старые jQuery-плагины используют:

.on("input", handler)

При этом внутри обработчика они могут напрямую менять:

input.value = formatted;

Inputmask в этот момент уже управляет буфером символов, поэтому возникают:

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

Проблемный пример:

$("#phone").on("input", function () {
    this.value = this.value.toUpperCase();
});

Inputmask не ожидает внешнего изменения value во время обработки собственного события.

Безопаснее использовать события Inputmask:

$("#phone").on("complete", function () {
    console.log("Маска заполнена");
});

Конфликты с React

Проблема controlled components

В React поле ввода часто становится controlled-компонентом:

<input value={value} onCha nge={handleChange} />

Inputmask одновременно пытается:

  • менять DOM напрямую
  • двигать caret
  • обновлять value

React же считает единственным источником истины состояние компонента.

Результат:

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

Типичная ошибка интеграции

useEffect(() => {
    Inputmask("+7 (999) 999-99-99").mask(inputRef.current);
}, []);

return (
    <input
        ref={inputRef}
        value={phone}
        onCha nge={(e) => setPhone(e.target.value)}
    />
);

React после каждого setPhone перерисовывает значение, разрушая внутренний буфер Inputmask.


Использование uncontrolled input

Наиболее стабильный вариант:

<input ref={inputRef} />

Инициализация:

useEffect(() => {
    const im = new Inputmask("+7 (999) 999-99-99");

    im.mask(inputRef.current);

    return () => {
        im.remove();
    };
}, []);

Конфликт re-render

Даже при использовании ref возможны проблемы, если компонент часто перерисовывается.

Например:

<input key={dynamicValue} ref={inputRef} />

Изменение key приводит к уничтожению DOM-узла и повторному созданию input.

Inputmask при этом:

  • теряет ссылки
  • сохраняет старые обработчики
  • создаёт утечки памяти

Интеграция через react-input-mask

Для React нередко используют специализированные обёртки, адаптирующие поведение маски под lifecycle React.

Однако при одновременном использовании:

  • Inputmask
  • react-input-mask
  • react-hook-form

может происходить двойная обработка ввода.


Конфликты с Vue

Реактивность Vue и DOM-мутации

Vue отслеживает изменения модели:

<input v-model="phone">

Inputmask изменяет значение напрямую через DOM API.

Vue может:

  • перезаписать маску
  • восстановить старое значение
  • вызвать лишний render

Конфликт v-model

Проблемный пример:

<input v-model="phone" ref="phone">
mounted() {
    Inputmask("+7 (999) 999-99-99").mask(this.$refs.phone);
}

Во время обновления phone Vue вызывает собственную синхронизацию, конфликтующую с Inputmask.


Решение через директивы

Обычно создают отдельную Vue-директиву:

app.directive("mask", {
    mounted(el, binding) {
        Inputmask(binding.value).mask(el);
    }
});

Использование:

<input v-mask="'+7 (999) 999-99-99'">

Такой подход лучше интегрируется в lifecycle Vue.


Конфликты с Angular

Zone.js и события Inputmask

Angular перехватывает асинхронные операции через Zone.js.

Inputmask генерирует множество событий:

  • keydown
  • keypress
  • input
  • composition
  • paste

Angular может запускать change detection слишком часто.

Результат:

  • лаги интерфейса
  • медленный ввод
  • высокое потребление CPU

Проблемы с FormControl

Пример:

<input formControlName="phone">

Если маска обновляет значение напрямую, Angular может не увидеть изменение модели.

Обратная ситуация тоже возможна:

  • Angular обновляет FormControl
  • Inputmask заново форматирует поле
  • происходит повторный цикл изменений

ExpressionChangedAfterItHasBeenCheckedError

Иногда Inputmask изменяет поле после Angular-проверки.

Это вызывает:

ExpressionChangedAfterItHasBeenCheckedError

Особенно часто проблема возникает при:

  • динамических масках
  • асинхронной загрузке формы
  • lazy rendering

Конфликты с библиотеками валидации

Inputmask и Parsley.js

Библиотеки валидации обычно проверяют:

input.value

Но значение Inputmask может содержать:

  • плейсхолдеры
  • служебные символы
  • незаполненные сегменты

Например:

+7 (123) ___-__-__

Валидатор может ошибочно считать поле заполненным.


Использование unmaskedvalue

Для корректной валидации применяют:

input.inputmask.unmaskedvalue()

Пример:

const raw = input.inputmask.unmaskedvalue();

Тогда валидатор получает только реальные символы:

71234567890

Конфликт автоматической очистки

Некоторые валидаторы очищают поле:

input.value = "";

Inputmask продолжает хранить внутренний буфер и может:

  • восстановить старое значение
  • сломать caret
  • оставить placeholder

Лучше использовать API самой маски:

input.inputmask.setValue("");

Конфликты с autocomplete-компонентами

Автоматическая подстановка значения

Autocomplete-компоненты часто выполняют:

input.value = selected;

Inputmask не всегда успевает синхронизировать:

  • caret
  • буфер
  • placeholder

Проблемы с адресами и телефонами

Особенно заметны конфликты при:

  • Google Places API
  • телефонных справочниках
  • банковских формах
  • CRM-системах

Autocomplete может вставить строку целиком, минуя посимвольный ввод.


Использование setValue

Безопасный вариант:

input.inputmask.setValue(selectedValue);

Inputmask корректно:

  • обновит буфер
  • перестроит маску
  • синхронизирует caret

Конфликты с IME и национальными раскладками

Input Method Editor

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

  • китайского
  • японского
  • корейского ввода

Во время composition-событий браузер работает иначе.

Inputmask может:

  • преждевременно форматировать ввод
  • ломать составные символы
  • удалять промежуточные значения

Composition Events

Особенно чувствительны события:

compositionstart
compositionupdate
compositionend

Если сторонняя библиотека также обрабатывает их, появляются:

  • дубли символов
  • повреждение текста
  • некорректный caret

Конфликты с форматтерами чисел

Одновременное форматирование

Частая ошибка — совместное использование:

  • Inputmask
  • AutoNumeric
  • Cleave.js
  • Numeral.js

Все эти библиотеки форматируют одно и то же поле.


Конфликт тысячных разделителей

Пример:

Inputmask("decimal", {
    groupSeparator: " "
}).mask(input);

И одновременно:

new AutoNumeric(input);

В результате:

  • число форматируется дважды
  • разделители дублируются
  • caret прыгает
  • ввод замедляется

Конфликты с Bootstrap и UI-компонентами

Bootstrap Modal может:

  • скрывать элементы
  • переносить DOM
  • переинициализировать контент

Если маска была создана до открытия модального окна:

$("#phone").inputmask(...);

после открытия modal возможны:

  • потеря фокуса
  • неправильный caret
  • неработающий placeholder

Инициализация после открытия

Стабильный подход:

$('#modal').on('shown.bs.modal', function () {
    $("#phone").inputmask("+7 (999) 999-99-99");
});

Конфликты с SSR

Серверный рендеринг

Inputmask зависит от:

  • window
  • document
  • DOM API

При SSR возникают ошибки:

window is not defined

Next.js

Проблемный код:

import Inputmask from "inputmask";

на сервере может завершиться ошибкой.


Динамический импорт

Используют:

useEffect(() => {
    import("inputmask").then(({ default: Inputmask }) => {
        Inputmask(...).mask(inputRef.current);
    });
}, []);

Так библиотека загружается только в браузере.


Конфликты с MutationObserver

Автоматическое отслеживание DOM

Некоторые библиотеки используют:

new MutationObserver(...)

и автоматически пересоздают компоненты.

Inputmask может:

  • повторно навесить обработчики
  • создать несколько экземпляров
  • оставить старые ссылки

Защита от двойной инициализации

Проверка:

if (!input.inputmask) {
    Inputmask(mask).mask(input);
}

Конфликты с Web Components

Shadow DOM

Inputmask не всегда корректно работает внутри Shadow DOM.

Проблемы:

  • потеря событий
  • неправильный focus
  • ошибки caret
  • недоступность внутренних элементов

Закрытый ShadowRoot

При mode: "closed" библиотека вообще не может получить доступ к элементам.


Конфликты мобильных браузеров

iOS Safari

iOS Safari по-своему обрабатывает:

  • selectionStart
  • virtual keyboard
  • input events

Inputmask иногда конфликтует с:

  • автозаменой
  • автокоррекцией
  • smart punctuation

Android Chrome

На Android часто возникают проблемы:

  • двойного ввода
  • задержек caret
  • повторных input events

Особенно при использовании:

inputmode="numeric"

совместно с numeric-mask.


Конфликты с собственными обработчиками

Ручное изменение value

Самый распространённый источник проблем:

input.value = format(input.value);

Inputmask не отслеживает прямые мутации всегда корректно.


Проблемы debounce

Например:

const handler = debounce(() => {
    input.value = normalize(input.value);
}, 300);

Через 300 мс значение внезапно меняется вне жизненного цикла Inputmask.

Результат:

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

Диагностика конфликтов

Симптомы конфликтов

Наиболее типичные признаки:

  • прыгающий курсор
  • невозможность удалить символ
  • дублирование текста
  • бесконечные события input
  • медленный ввод
  • утечки памяти
  • зависания формы
  • исчезновение placeholder
  • сброс маски
  • ввод только части значения

Проверка количества обработчиков

При множественной инициализации можно увидеть:

console.log(input.inputmask);

или:

getEventListeners(input)

Проверка жизненного цикла

Важно отслеживать:

  • создание элемента
  • уничтожение элемента
  • повторный render
  • смену DOM-узла
  • изменение key
  • mount/unmount

Стратегии предотвращения конфликтов

Один источник управления value

Поле должно иметь только одного владельца:

  • либо Inputmask
  • либо formatter
  • либо framework-controlled state

Одновременное управление почти всегда приводит к ошибкам.


Использование официальных адаптеров

Для крупных фреймворков предпочтительнее:

  • React wrappers
  • Angular directives
  • Vue plugins

Они учитывают lifecycle конкретной платформы.


Корректное уничтожение экземпляров

Перед удалением элемента:

input.inputmask.remove();

Иначе остаются:

  • обработчики
  • ссылки
  • таймеры
  • внутренние буферы

Отказ от прямого изменения DOM

Нежелательно:

input.value = value;

Предпочтительно:

input.inputmask.setValue(value);

Изоляция ответственности

Форматирование, валидация и реактивность не должны одновременно изменять текст поля.

Оптимальная схема:

  • Inputmask отвечает за ввод
  • validator отвечает за проверку
  • framework отвечает за состояние
  • formatter не вмешивается в DOM input

Отложенная инициализация

Для динамических интерфейсов полезно:

requestAnimationFrame(() => {
    Inputmask(mask).mask(input);
});

или:

setTimeout(() => {
    Inputmask(mask).mask(input);
}, 0);

Это снижает вероятность конфликта с lifecycle UI-фреймворка.


Использование событий Inputmask

Вместо прямого отслеживания input:

input.addEventListener("complete", handler);

или:

oncomplete: function () {

}

Так уменьшается вмешательство в низкоуровневую обработку ввода.