Привязка к элементам input

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

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

Базовое связывание с одиночным input

Минимальная форма привязки предполагает передачу DOM-элемента в конструктор:

import Cleave from 'cleave.js';

const input = document.querySelector('#phone');

const cleave = new Cleave(input, {
    phone: true,
    phoneRegionCode: 'KZ'
});

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

Ключевой момент: библиотека не копирует значение в отдельное состояние, а работает поверх DOM-узла.

Привязка через CSS-селектор

Допустим сценарий, когда элемент не требуется заранее сохранять в переменную:

new Cleave('#credit-card', {
    creditCard: true
});

Внутри происходит поиск элемента через document.querySelector. Это упрощённая форма инициализации, которая используется для одиночных элементов без дополнительной логики управления.

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

Множественные input-элементы

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

const inputs = document.querySelectorAll('.date-field');

inputs.forEach((el) => {
    new Cleave(el, {
        date: true,
        datePattern: ['d', 'm', 'Y']
    });
});

Каждый input получает собственный экземпляр. Это означает изоляцию состояния: форматирование одного поля не влияет на другое.

Особенности жизненного цикла привязки

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

  • input
  • keydown
  • focus
  • cut
  • copy
  • paste

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

Привязка сохраняется до тех пор, пока экземпляр не будет уничтожен вручную:

cleave.destroy();

После вызова destroy происходит:

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

Привязка к динамически создаваемым элементам

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

function createPhoneInput() {
    const input = document.createElement('input');
    input.id = 'dynamic-phone';
    document.body.appendChild(input);

    new Cleave(input, {
        phone: true,
        phoneRegionCode: 'KZ'
    });
}

Ключевое условие — элемент должен существовать в DOM в момент передачи в конструктор. Попытка инициализации до вставки приводит к отсутствию корректной привязки.

Повторная инициализация и защита от дублирования

Частая проблема при интеграции — повторное создание экземпляра на одном и том же input. Это происходит, например, при повторном рендере компонента или повторном вызове функции инициализации.

const input = document.querySelector('#amount');

if (!input.cleaveInstance) {
    input.cleaveInstance = new Cleave(input, {
        numeral: true,
        numeralThousandsGroupStyle: 'thousand'
    });
}

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

Альтернативный подход — явное уничтожение перед повторной инициализацией:

if (input.cleaveInstance) {
    input.cleaveInstance.destroy();
}

input.cleaveInstance = new Cleave(input, {
    numeral: true
});

Привязка в момент фокуса

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

const input = document.querySelector('#lazy-format');

let cleaveInstance = null;

input.addEventListener('focus', () => {
    if (!cleaveInstance) {
        cleaveInstance = new Cleave(input, {
            date: true,
            datePattern: ['d', 'm', 'Y']
        });
    }
});

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

Связь с значением input и model-value

Cleave работает напрямую с value DOM-элемента. Это означает, что любые внешние изменения через JavaScript должны учитывать возможное вмешательство форматтера.

input.value = '1234567890';
input.dispatchEvent(new Event('input'));

Без генерации события input библиотека может не синхронизировать внутреннее состояние с новым значением.

Привязка в условиях перерисовки интерфейса

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

При удалении input из DOM:

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

Типовой паттерн:

let cleaveInstance = null;

function mount() {
    const input = document.querySelector('#spa-input');

    cleaveInstance = new Cleave(input, {
        numeral: true
    });
}

function unmount() {
    if (cleaveInstance) {
        cleaveInstance.destroy();
        cleaveInstance = null;
    }
}

Работа с form-reset и внешними изменениями

Сброс формы через form.reset() не всегда приводит к корректному обновлению состояния форматтера. В таких случаях требуется принудительная синхронизация:

const form = document.querySelector('form');
const input = document.querySelector('#price');

const cleave = new Cleave(input, {
    numeral: true
});

form.addEventListener('reset', () => {
    setTimeout(() => {
        cleave.setRawValue('');
    }, 0);
});

Метод setRawValue позволяет установить «чистое» значение без форматирования и восстановить согласованное состояние между DOM и внутренним состоянием библиотеки.

Привязка нескольких экземпляров к одному input (антипаттерн)

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

new Cleave('#input', { numeral: true });
new Cleave('#input', { creditCard: true });

Каждый экземпляр устанавливает свои обработчики и перезаписывает поведение ввода. В результате:

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

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

Привязка через обёртки и кастомные функции инициализации

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

function bindCleave(el, config) {
    if (el.cleaveInstance) {
        el.cleaveInstance.destroy();
    }

    el.cleaveInstance = new Cleave(el, config);

    return el.cleaveInstance;
}

bindCleave(document.querySelector('#phone'), {
    phone: true,
    phoneRegionCode: 'KZ'
});

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

Особенности привязки в контексте атрибутов data-*

Некоторые реализации используют данные из HTML-атрибутов для конфигурации:

<input id="phone" data-format="phone" data-region="KZ">
const input = document.querySelector('#phone');

const format = input.dataset.format;
const region = input.dataset.region;

new Cleave(input, {
    phone: format === 'phone',
    phoneRegionCode: region
});

Такой подход позволяет отделить конфигурацию от JavaScript-логики и централизовать поведение на уровне разметки.

Синхронизация с внешними обработчиками событий

При наличии сторонних слушателей на input важно учитывать порядок выполнения:

  • Cleave перехватывает ввод
  • затем обновляет value
  • затем срабатывают внешние обработчики
input.addEventListener('input', (e) => {
    console.log('formatted:', e.target.value);
});

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

Привязка и доступ к экземпляру через DOM

Для удобства управления экземпляр часто сохраняется в свойстве DOM-элемента:

const input = document.querySelector('#sum');

input.cleaveInstance = new Cleave(input, {
    numeral: true
});

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

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