Комбинирование нескольких экземпляров

Использование нескольких экземпляров форматтера в рамках одной страницы возникает в интерфейсах с комплексными формами: платежные системы, профили пользователей, административные панели, конструкторы документов. Cleave.js в таких сценариях работает как независимый слой над каждым DOM-элементом, однако при проектировании поведения требуется учитывать взаимодействие между экземплярами, порядок инициализации, жизненный цикл и возможную синхронизацию состояния.

Каждый экземпляр Cleave.js представляет собой изолированную сущность, привязанную к конкретному input-элементу. Внутренне он хранит ссылку на DOM, конфигурацию форматирования и текущее состояние строки. При наличии нескольких экземпляров ключевым становится управление их жизненным циклом и координация поведения при изменении общих данных.

Изоляция экземпляров и независимое форматирование

Базовая модель работы Cleave.js предполагает отсутствие перекрёстных зависимостей между инстансами. Каждый объект:

  • обрабатывает собственный input-event поток;
  • применяет собственные правила форматирования;
  • хранит локальное состояние значения;
  • не имеет встроенного механизма наблюдения за другими экземплярами.

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

const cardNumber = new Cleave('#card-number', {
  creditCard: true
});

const expiryDate = new Cleave('#expiry-date', {
  date: true,
  datePattern: ['m', 'y']
});

const phone = new Cleave('#phone', {
  phone: true,
  phoneRegionCode: 'US'
});

Каждый экземпляр действует автономно, даже если они находятся внутри одной формы или логического блока.

Общие конфигурации и фабрики экземпляров

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

const baseDateConfig = {
  date: true,
  delimiter: '/',
  datePattern: ['d', 'm', 'Y']
};

const instances = [];

document.querySelectorAll('.date-input').forEach((el) => {
  instances.push(new Cleave(el, baseDateConfig));
});

Такой подход снижает вероятность расхождения форматов между полями и упрощает масштабирование интерфейса.

При этом важно учитывать, что конфигурация передаётся по значению, а не по ссылке состояния. Изменение объекта после инициализации не влияет на уже созданные экземпляры.

Управление жизненным циклом экземпляров

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

Основные операции жизненного цикла:

  • инициализация;
  • обновление конфигурации через destroy и повторное создание;
  • уничтожение экземпляра при удалении DOM-элемента.
let instance = new Cleave('#dynamic-field', {
  numeral: true
});

// пересоздание с новыми параметрами
instance.destroy();

instance = new Cleave('#dynamic-field', {
  numeral: true,
  numeralDecimalScale: 2
});

Отсутствие явного управления жизненным циклом в сложных интерфейсах приводит к накоплению неактуальных обработчиков событий и рассинхронизации состояния DOM и логики форматирования.

Синхронизация значений между экземплярами

В ряде сценариев требуется синхронное обновление нескольких полей. Cleave.js не предоставляет встроенного механизма связей, поэтому синхронизация реализуется на уровне внешней логики.

Типичный пример — дублирование значения в различных форматах:

const source = new Cleave('#source', {
  numeral: true
});

const formatted = new Cleave('#formatted', {
  numeral: true,
  numeralThousandsGroupStyle: 'thousand'
});

document.querySelector('#source').addEventListener('input', (e) => {
  const value = e.target.value;
  document.querySelector('#formatted').value = value;
});

В этом подходе Cleave.js остаётся исключительно слоем форматирования, а координация данных выносится во внешнюю логику.

При необходимости более строгой синхронизации применяется промежуточный слой состояния (state layer), который хранит «сырое» значение и распределяет его между экземплярами.

Центральное состояние и координация экземпляров

В сложных формах несколько Cleave-инстансов часто работают с одним логическим значением, представленным в разных форматах. Например:

  • номер телефона в локальном формате;
  • международный формат;
  • отображение без маски для API.

В таких случаях используется централизованное хранилище состояния.

const state = {
  phone: ''
};

const phoneLocal = new Cleave('#phone-local', {
  phone: true,
  onValueChanged: (e) => {
    state.phone = e.target.rawValue;
    phoneIntl.setRawValue(state.phone);
  }
});

const phoneIntl = new Cleave('#phone-intl', {
  phone: true,
  phoneRegionCode: 'GB',
  onValueChanged: (e) => {
    state.phone = e.target.rawValue;
    phoneLocal.setRawValue(state.phone);
  }
});

Такая схема требует аккуратного предотвращения циклических обновлений. Обычно вводится флаг блокировки или сравнение значений перед применением обновления.

Предотвращение циклических обновлений

При взаимной синхронизации нескольких экземпляров возникает риск бесконечных циклов обновления. Для контроля используется механизм «тихого обновления» или временного отключения обработчиков.

let isSyncing = false;

const instanceA = new Cleave('#a', {
  numeral: true,
  onValueChanged: (e) => {
    if (isSyncing) return;
    isSyncing = true;

    instanceB.setRawValue(e.target.rawValue);

    isSyncing = false;
  }
});

const instanceB = new Cleave('#b', {
  numeral: true,
  onValueChanged: (e) => {
    if (isSyncing) return;
    isSyncing = true;

    instanceA.setRawValue(e.target.rawValue);

    isSyncing = false;
  }
});

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

Динамическое создание групп экземпляров

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

function createCardBlock(container) {
  const number = new Cleave(container.querySelector('.card-number'), {
    creditCard: true
  });

  const expiry = new Cleave(container.querySelector('.card-expiry'), {
    date: true,
    datePattern: ['m', 'y']
  });

  return { number, expiry };
}

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

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

При большом количестве инстансов удобнее хранить их в коллекции, что упрощает массовые операции:

  • сброс значений;
  • обновление конфигурации;
  • уничтожение всех экземпляров;
  • переключение режимов форматирования.
const cleaveInstances = [];

function addInstance(selector, config) {
  const instance = new Cleave(selector, config);
  cleaveInstances.push(instance);
  return instance;
}

function destroyAll() {
  cleaveInstances.forEach(i => i.destroy());
  cleaveInstances.length = 0;
}

Такой подход особенно эффективен в формах, которые пересоздаются при навигации между шагами.

Разделение ответственности между экземплярами

При проектировании систем с множеством форматтеров важно избегать перекрёстного влияния логики форматирования. Каждый экземпляр должен:

  • отвечать только за собственное поле;
  • не изменять DOM других элементов напрямую;
  • не хранить глобальное состояние;
  • получать данные извне через явные вызовы.

Любая попытка встроить межэкземплярную логику внутрь Cleave.js приводит к усложнению поддержки и снижению предсказуемости поведения.

Производительность при множественных экземплярах

При увеличении числа экземпляров возрастает нагрузка на обработку событий ввода. Основные факторы влияния:

  • количество input-обработчиков;
  • сложность маски;
  • частота перерасчёта значения;
  • повторные перерисовки DOM.

Оптимизация достигается за счёт:

  • использования одинаковых конфигураций;
  • минимизации пересозданий экземпляров;
  • уничтожения неиспользуемых инстансов;
  • отказа от избыточной синхронизации.

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

Комбинирование различных типов форматирования

В рамках одной формы часто используются разные типы Cleave-конфигураций: числовые, датированные, телефонные и кастомные маски. Совмещение таких экземпляров требует согласованного подхода к обработке raw-значений.

const amount = new Cleave('#amount', {
  numeral: true
});

const date = new Cleave('#date', {
  date: true,
  datePattern: ['d', 'm', 'Y']
});

const iban = new Cleave('#iban', {
  delimiters: [' '],
  blocks: [4, 4, 4, 4, 4, 4]
});

Несмотря на различие форматов, все экземпляры могут быть объединены в единую систему отправки данных, где используется единый механизм извлечения rawValue.

Управление ошибками и деградация поведения

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

Рекомендуется централизованная проверка наличия элементов перед инициализацией и отказ от создания инстанса при отсутствии целевого узла.

При ошибках отдельных экземпляров остальные продолжают функционировать независимо, что является следствием изолированной архитектуры Cleave.js.

Объединение экземпляров в модульные блоки

В крупных приложениях экземпляры часто группируются по функциональным блокам: платежные данные, контактная информация, адреса доставки. Такая группировка позволяет оперировать не отдельными инстансами, а логическими модулями.

function PaymentModule(root) {
  this.card = new Cleave(root.querySelector('.card'), { creditCard: true });
  this.expiry = new Cleave(root.querySelector('.expiry'), { date: true });
  this.cvv = new Cleave(root.querySelector('.cvv'), { numeral: true });
}

PaymentModule.prototype.destroy = function () {
  this.card.destroy();
  this.expiry.destroy();
  this.cvv.destroy();
};

Подобная структура облегчает масштабирование интерфейсов и управление сложными формами с множеством взаимосвязанных полей.