Обработка событий onChange

В Cleave.js обработка изменений значения устроена иначе, чем в стандартных DOM-инпутов. Библиотека не полагается исключительно на нативное событие onchange или oninput, а вводит собственный уровень абстракции через механизм форматирования и перехвата значения. Центральным элементом становится callback onValueChanged, который вызывается при каждом изменении внутреннего состояния поля ввода.


Различие между нативным onChange и onValueChanged

В стандартном JavaScript поведение инпута определяется DOM-событиями:

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

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

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

Поэтому вместо прямого использования onchange используется:

  • onValueChanged — основной механизм отслеживания изменений

Основная сигнатура onValueChanged

При инициализации Cleave.js callback передаётся через конфигурацию:

const cleave = new Cleave(inputElement, {
    phone: true,
    onValueChanged: function(e) {
        // логика обработки
    }
});

Параметр e содержит структурированную информацию о состоянии поля:

  • e.target.value — отформатированное значение
  • e.target.rawValue — «чистое» значение без маски
  • дополнительные внутренние данные о форматировании

Форматированное и «сырое» значение

Одной из ключевых особенностей Cleave.js является разделение данных на два слоя:

Форматированное значение

Это то, что видит пользователь:

+7 (777) 123-45-67

Оно предназначено исключительно для отображения и улучшения UX.

Raw value

Это значение без форматирования:

77771234567

Именно rawValue используется для:

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

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

const cleave = new Cleave('#phone', {
    phone: true,
    phoneRegionCode: 'KZ',
    onValueChanged: function(e) {
        console.log('Formatted:', e.target.value);
        console.log('Raw:', e.target.rawValue);
    }
});

В этом примере каждый ввод пользователя приводит к:

  1. переформатированию строки
  2. обновлению DOM-значения
  3. вызову onValueChanged

Частота вызовов и особенности исполнения

onValueChanged вызывается:

  • при вводе символа
  • при удалении символа
  • при вставке (paste)
  • при программном изменении значения через API Cleave

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

Следствие:

  • внутри callback не рекомендуется выполнять тяжёлые операции
  • необходимо избегать синхронных сетевых запросов
  • предпочтительна минимальная логика (например, обновление состояния UI)

Работа с курсором и побочные эффекты

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

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

В контексте onValueChanged это приводит к важному эффекту:

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

Поэтому любые манипуляции с DOM внутри onValueChanged могут конфликтовать с внутренним механизмом библиотеки.


Использование onChange в связке с Cleave.js

Хотя Cleave.js использует собственный callback, нативное событие change или input всё ещё может быть полезно.

Однако есть нюанс:

  • onChange DOM срабатывает уже после форматирования
  • Cleave.js может изменить значение несколько раз за один ввод

Пример совмещения:

inputElement.addEventListener('change', (e) => {
    console.log('Native change:', e.target.value);
});

Но в реальной практике:

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

Сценарий с валидацией в реальном времени

Cleave.js часто используется совместно с валидацией формы. В таком случае onValueChanged становится точкой синхронизации:

const cleave = new Cleave('#card', {
    creditCard: true,
    onValueChanged: function(e) {
        const raw = e.target.rawValue;

        if (raw.length < 16) {
            setError('Номер карты слишком короткий');
        } else {
            clearError();
        }
    }
});

Особенности такого подхода:

  • валидация выполняется на каждом вводе
  • проверяется именно rawValue, а не отображаемая строка
  • UI-ошибки обновляются в реальном времени

Интеграция с состоянием приложения

При использовании в SPA (React, Vue, Angular) onValueChanged часто становится мостом между DOM и состоянием приложения.

Пример общей логики

onValueChanged: function(e) {
    const rawValue = e.target.rawValue;

    updateState({
        phone: rawValue
    });
}

Важно учитывать:

  • форматированное значение не должно попадать в state
  • state должен хранить только нормализованные данные
  • форматирование — исключительно ответственность Cleave.js

Частые ошибки при работе с onValueChanged

1. Хранение formatted value в состоянии

Ошибка:

setValue(e.target.value);

Проблема:

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

2. Выполнение тяжёлой логики внутри callback

Ошибка:

  • API-запросы на каждый ввод
  • сложные вычисления

Правильнее:

  • использовать debounce
  • или переносить обработку в отдельный слой

3. Конфликт с управляемыми инпутами

При использовании controlled input (например, в React):

  • Cleave.js может перезаписывать значение
  • React может перерендеривать input

Это требует аккуратной синхронизации value и rawValue.


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

Чтобы снизить нагрузку, часто применяется задержка обработки:

let timeout;

onValueChanged: function(e) {
    clearTimeout(timeout);

    timeout = setTimeout(() => {
        const raw = e.target.rawValue;
        validate(raw);
    }, 300);
}

Это особенно важно для:

  • поиска по вводу
  • запросов к серверу
  • сложной валидации

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

Cleave.js вызывает onValueChanged не только при пользовательском вводе, но и при:

cleave.setRawValue('77771234567');

или

cleave.setValue('+7 777 123 45 67');

В таких случаях:

  • callback срабатывает синхронно
  • форматирование применяется автоматически
  • состояние всегда остаётся согласованным

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

Логика можно представить как последовательность:

  1. Пользователь вводит символ
  2. Cleave.js перехватывает ввод
  3. Применяется маска и форматирование
  4. Обновляется DOM-значение
  5. Пересчитывается курсор
  6. Вызывается onValueChanged
  7. Передаются value и rawValue

Эта цепочка делает onValueChanged ключевой точкой интеграции Cleave.js с внешними системами, заменяя классический onChange в большинстве сценариев использования.