Кастомные обработчики событий

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

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


Основные типы событий в AutoNumeric

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

Нативные DOM-события

  • input — изменение значения в процессе ввода
  • change — фиксация значения после потери фокуса
  • focus — активация поля ввода
  • blur — потеря фокуса
  • keydown, keyup — обработка клавиатурного ввода

События, связанные с форматированием

AutoNumeric перехватывает ввод и изменяет поведение стандартных событий, добавляя этапы:

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

Внутренние события библиотеки

В зависимости от конфигурации и версии, могут использоваться события уровня API:

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

Архитектура обработчиков событий

Обработчики в AutoNumeric можно разделить на три уровня:

1. DOM-слой

Прямое навешивание обработчиков на input-элемент:

element.addEventListener('input', handler);
element.addEventListener('blur', handler);

Этот слой отвечает за первичный захват пользовательского ввода.

2. Слой библиотеки

AutoNumeric перехватывает события и применяет внутренние преобразования:

  • извлечение “сырых” данных
  • удаление разделителей тысяч
  • нормализация десятичного разделителя
  • проверка допустимых символов

3. Пользовательские колбэки

Колбэки, передаваемые через конфигурацию:

AutoNumeric.set(input, {
  onChange: (currentValue, element) => {
    console.log(currentValue);
  }
});

Конфигурационные обработчики событий

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

onInvalidPaste

Срабатывает при попытке вставки некорректного значения.

AutoNumeric.set(input, {
  onInvalidPaste: (clipboardData, element) => {
    return false;
  }
});

Возвращаемое значение позволяет:

  • блокировать вставку
  • модифицировать входные данные
  • пропускать стандартную обработку

onFocus и onBlur

Позволяют управлять состоянием отображения:

AutoNumeric.set(input, {
  onFocus: (event, element) => {
    element.classList.add('active');
  },
  onBlur: (event, element) => {
    element.classList.remove('active');
  }
});

Используются для:

  • переключения UI-состояний
  • временного изменения формата
  • логирования активности поля

onChange

Срабатывает при изменении уже нормализованного значения:

AutoNumeric.set(input, {
  onChange: (value, element) => {
    updateModel(value);
  }
});

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


Программные события через API экземпляра

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

const an = new AutoNumeric(input, {
  decimalPlaces: 2
});

an.set(1234.56);

Изменение значения через set() может вызывать цепочку событий:

  • внутреннее обновление состояния
  • обновление DOM
  • вызов пользовательских колбэков

Перехват событий клавиатуры

Обработка клавиатурного ввода является критическим компонентом системы:

AutoNumeric.set(input, {
  onKeyDown: (event, element) => {
    if (event.key === '-') {
      return true;
    }
    return false;
  }
});

Механизм позволяет:

  • блокировать ввод недопустимых символов
  • разрешать специальные клавиши (backspace, delete)
  • модифицировать поведение стрелок

Кастомные обработчики через addEventListener

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

const an = new AutoNumeric(input);

input.addEventListener('input', () => {
  const rawValue = an.getNumericString();
  console.log(rawValue);
});

Такой подход применяется для:

  • интеграции с внешними библиотеками
  • синхронизации с реактивными системами
  • построения собственных слоёв валидации

Управление порядком обработки событий

AutoNumeric влияет на порядок выполнения событий, внедряясь между фазами:

  1. пользовательский ввод
  2. перехват библиотекой
  3. нормализация
  4. форматирование
  5. повторная запись в DOM
  6. вызов пользовательских обработчиков

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


Предотвращение конфликтов событий

При интеграции с другими библиотеками (React, Vue, Angular) возникают ситуации дублирования событий.

Распространённые проблемы:

  • двойной input при controlled компонентах
  • конфликт форматирования
  • рассинхронизация состояния

Типовой способ минимизации конфликтов:

AutoNumeric.set(input, {
  watchExternalChanges: true,
  modifyValueOnWheel: false
});

Также применяется ручное подавление событий:

let internalUpdate = false;

input.addEventListener('input', (e) => {
  if (internalUpdate) return;

  const value = an.getNumericString();
  sync(value);
});

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

Хотя библиотека не всегда генерирует полноценные CustomEvent, возможно создание собственной системы поверх AutoNumeric:

function emitValueChange(element, value) {
  element.dispatchEvent(new CustomEvent('autonumeric:change', {
    detail: { value }
  }));
}

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

  • построения архитектуры событийного брокера
  • интеграции с глобальным стором
  • реактивных обновлений интерфейса

Обработка событий вставки (paste)

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

AutoNumeric.set(input, {
  onInvalidPaste: (clipboard, element) => {
    const cleaned = clipboard.replace(/[^\d.-]/g, '');
    return cleaned;
  }
});

Типичная цепочка:

  • захват clipboard data
  • фильтрация символов
  • проверка формата
  • вставка результата
  • повторное форматирование

Делегирование событий

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

document.addEventListener('input', (e) => {
  if (e.target.matches('.auto')) {
    const instance = AutoNumeric.getAutoNumericElement(e.target);
    console.log(instance.getNumber());
  }
});

Подход полезен при:

  • списках форм
  • SPA интерфейсах
  • таблицах с динамическими строками

Уничтожение обработчиков и очистка

При удалении элементов важно корректно освобождать события:

const an = new AutoNumeric(input);

an.wipe();
input.removeEventListener('input', handler);

Игнорирование очистки приводит к:

  • утечкам памяти
  • “висящим” обработчикам
  • неконсистентному состоянию DOM

Паттерны построения событийной логики

На практике используются несколько устойчивых подходов:

1. Централизованная синхронизация

Все изменения проходят через единый обработчик:

onChange: (value) => store.update(value);

2. Двухфазная обработка

Разделение:

  • capture: фильтрация
  • bubble: синхронизация

3. Адаптер событий

Обёртка над AutoNumeric:

function createNumericField(input) {
  const an = new AutoNumeric(input);

  input.addEventListener('input', () => {
    emit(an.getNumericString());
  });

  return an;
}

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

Изменение значения через API не всегда эквивалентно пользовательскому вводу. Это требует различения источников событий:

  • пользовательский ввод → input
  • set() → внутреннее обновление
  • upd ate() → синхронизация состояния

Контроль источника позволяет избежать циклических обновлений:

let source = 'user';

an.se t(100);
source = 'system';

Производительность событийной системы

При высокой частоте ввода критично:

  • минимизировать пересчёт форматирования
  • избегать лишних DOM-операций
  • использовать debounce для внешних синхронизаций
let timeout;

input.addEventListener('input', () => {
  clearTimeout(timeout);
  timeout = setTimeout(() => {
    sync(an.getNumericString());
  }, 100);
});

Оптимизация особенно важна в больших формах и таблицах с множественными инстансами AutoNumeric.