Проблемы совместимости браузеров

Базовая модель совместимости Cleave.js

Cleave.js ориентирован на работу с DOM-событиями ввода и преобразование пользовательского ввода в реальном времени. В основе библиотеки лежит перехват событий input, keydown, paste, а также обработка состояния поля ввода через стандартный API HTMLInputElement.

Ключевой принцип совместимости заключается в том, что библиотека минимально зависит от современных API браузеров и опирается на относительно стабильные механизмы:

  • addEventListener
  • input.value
  • события клавиатуры и вставки
  • базовые DOM-операции

Однако даже при такой консервативной модели возникают существенные различия в поведении браузеров, особенно при работе с мобильными устройствами, устаревшими движками и нестандартными режимами ввода.


Internet Explorer и устаревшие движки

Наиболее проблемная зона — старые версии Internet Explorer (IE10–IE11) и движки, близкие к ним по архитектуре.

Основные ограничения:

  • Отсутствие современных событий ввода в полной форме

    • событие input работает нестабильно в некоторых сценариях удаления символов
  • Различия в поведении event.which и event.keyCode

    • Cleave.js вынужден учитывать оба варианта
  • Проблемы с addEventListener в старых режимах совместимости

  • Отсутствие поддержки Array.from, Object.assign в нативной среде

    • требуется транспиляция или полифиллы

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


Edge Legacy и переходный период движка

EdgeHTML (старый Microsoft Edge) демонстрирует поведение, близкое к IE, но с частичной поддержкой современных API.

Проблемные моменты:

  • нестабильная работа selectionStart и selectionEnd при автоматическом форматировании
  • задержки обновления значения input после программного изменения
  • различия в обработке paste событий

Cleave.js в таких условиях может создавать визуальные артефакты: скачки курсора, повторное форматирование строки, кратковременные неконсистентные состояния input.


Safari (desktop и iOS): особенности event loop и input handling

Safari является одним из наиболее проблемных браузеров для библиотек маскирования ввода.

Основные причины:

1. Асинхронное обновление value

Safari часто обновляет input.value не синхронно с событием input, что приводит к ситуации:

  • событие уже сработало
  • значение поля ещё не обновлено

Это вызывает некорректное форматирование при быстром наборе.


2. Поведение на iOS (особенно Safari Mobile)

На мобильном Safari добавляются дополнительные ограничения:

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

Особенно критично:

  • автокоррекция
  • интеллектуальная замена символов
  • ввод через системные клавиатуры (IME)

Cleave.js вынужден часто пересчитывать позицию курсора, что в iOS не всегда возможно точно.


3. Ограничения selection API

Safari иногда:

  • возвращает selectionStart = null
  • сбрасывает выделение при программной модификации value
  • некорректно обрабатывает setSelectionRange после изменения DOM

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


Chrome и Chromium-браузеры

Chrome считается наиболее стабильной платформой для Cleave.js, однако и здесь есть тонкости.

1. Composition Events (IME)

При вводе азиатских языков (японский, китайский, корейский):

  • активируется режим composition
  • символы формируются до финального подтверждения

Cleave.js должен игнорировать промежуточные значения, иначе возникают:

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

2. Autofill и встроенные менеджеры паролей

Chrome может:

  • вставлять значение без генерации keydown
  • обновлять input.value напрямую

Это приводит к тому, что Cleave.js не получает сигнал о изменении и не форматирует значение до следующего события.


3. Optimized Input Events

Современные версии Chromium используют оптимизированные события ввода, где:

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

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


Firefox: точность событий и нестабильность каретки

Firefox отличается более строгой реализацией спецификации, но имеет собственные особенности.

1. Поведение selectionStart

В Firefox значение selectionStart чаще всего корректно, однако:

  • при мгновенном изменении value через JS может временно сбрасываться каретка
  • при быстром вводе возможны “скачки” позиции курсора

2. Отличия в обработке paste

Событие paste в Firefox:

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

Это приводит к необходимости двойной проверки значения после вставки.


Мобильные браузеры и виртуальные клавиатуры

На мобильных устройствах проблема совместимости становится системной из-за внешнего управления вводом.

1. Android Chrome

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

  • различное поведение разных клавиатур (Gboard, Samsung Keyboard)
  • возможна подмена символов до отправки в input
  • нестабильные keydown события
2. iOS Safari
  • частичная блокировка программного изменения caret
  • задержки обновления input value
  • отсутствие гарантии последовательности событий

Shadow DOM и изолированные окружения

При использовании Cleave.js внутри Web Components возникают дополнительные ограничения:

  • querySelector не видит элементы внутри shadow root без явного доступа
  • события могут не всплывать в привычном виде (особенно с composed: false)
  • требуется ручная привязка обработчиков к конкретному root

Это приводит к необходимости явно инициализировать Cleave.js на каждом input внутри shadow tree.


React, Vue и управляемые компоненты

Хотя Cleave.js является DOM-ориентированной библиотекой, при интеграции в реактивные фреймворки появляются конфликты:

1. React controlled input

React может:

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

В результате Cleave.js:

  • форматирует значение
  • React тут же возвращает “старое” значение

Это вызывает эффект дрожания input.


2. Vue reactivity system

Vue отслеживает изменения через Proxy/Object.defineProperty:

  • любые изменения value могут быть перезаписаны реактивной системой
  • возможны циклы обновления между Cleave.js и v-model

Пассивные события и производительность

В современных браузерах некоторые события помечаются как passive, особенно на touch-устройствах.

Это влияет на:

  • невозможность вызова preventDefault
  • ограничение контроля над scroll и input behavior

Cleave.js в таких условиях работает в более “наблюдательном” режиме, снижая уровень вмешательства в поток событий.


Форматирование и расхождения в Unicode

Дополнительная проблема совместимости связана не столько с браузерами, сколько с Unicode-реализацией:

  • разные браузеры по-разному интерпретируют комбинированные символы
  • emoji могут занимать 2–4 code unit
  • length строки (value.length) не отражает визуальную длину

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


Итоговая модель проблем совместимости

Поведение Cleave.js в разных браузерах формируется пересечением трёх факторов:

  • различия в event loop и input events
  • особенности управления кареткой и selection API
  • внешние системы ввода (IME, автозаполнение, виртуальные клавиатуры)

Из-за этого одна и та же логика форматирования может давать разные результаты в зависимости от окружения, даже при идентичном коде.