Архитектура событий в библиотеке строится вокруг системы хуков (hooks), позволяющей подключать несколько функций к одному и тому же событию без необходимости перезаписи предыдущих обработчиков. В основе лежит модель, где каждый жизненный этап календаря — открытие, закрытие, выбор даты, смена месяца или года — представляет собой точку расширения поведения.
Основной способ реализации множественных обработчиков заключается в передаче массива функций в конфигурации инициализации. Каждый элемент массива рассматривается как независимый обработчик, вызываемый последовательно.
const fp = flatpickr("#input", {
onChange: [
function(selectedDates, dateStr, instance) {
console.log("Первый обработчик:", dateStr);
},
function(selectedDates, dateStr, instance) {
console.log("Второй обработчик:", selectedDates);
}
]
});
Последовательность выполнения определяется порядком объявления. Внутренний механизм не прерывает цепочку, если один из обработчиков не вызывает исключение, что делает модель предсказуемой при композиции логики.
После инициализации экземпляра возможно расширение поведения без пересоздания календаря. Обработчики могут добавляться в уже существующий массив.
fp.config.onOpen.push(function(selectedDates, dateStr, instance) {
console.log("Дополнительный обработчик открытия");
});
Такой подход позволяет внедрять функциональность на этапе выполнения, например при загрузке модулей или подключении плагинов.
Важно учитывать, что прямое изменение config влияет на
глобальную конфигурацию экземпляра, а не только на единичный вызов
события.
При необходимости строгого контроля порядка выполнения применяется композиционный подход, где исходный обработчик оборачивается дополнительной логикой.
const originalOnChange = fp.config.onChange;
fp.set("onChange", [
function(...args) {
console.log("До основного обработчика");
},
...originalOnChange,
function(...args) {
console.log("После основного обработчика");
}
]);
Такая структура позволяет явно управлять стадиями выполнения: предварительная обработка, основная логика и постобработка.
Каждый обработчик получает одинаковый набор аргументов:
selectedDates — массив выбранных датdateStr — строковое представление значенияinstance — экземпляр календаряКонтекст instance играет ключевую роль при работе с
множественными обработчиками, так как позволяет каждому из них
независимо взаимодействовать с состоянием компонента.
onClose: [
function(selectedDates, dateStr, instance) {
instance.clear(); // один из обработчиков изменяет состояние
},
function(selectedDates, dateStr, instance) {
console.log(instance.selectedDates);
}
]
При использовании нескольких обработчиков возникает зависимость от порядка их выполнения. Это особенно критично, если один обработчик модифицирует состояние, используемое другим.
onChange: [
function(dates, str, instance) {
instance.config.enableTime = true;
},
function(dates, str, instance) {
console.log(instance.config.enableTime);
}
]
Во втором обработчике значение уже может быть изменено первым, что формирует цепочку зависимостей.
Помимо встроенных хуков, экземпляр календаря взаимодействует с DOM-элементом, на который он привязан. Это позволяет добавлять стандартные обработчики событий параллельно с внутренними механизмами.
const input = document.querySelector("#input");
input.addEventListener("change", () => {
console.log("DOM change обработчик");
});
Такой подход существует параллельно с системой хуков и не заменяет её. В результате событие может быть обработано дважды: внутри календаря и на уровне DOM.
При увеличении количества обработчиков целесообразно выносить их в отдельные функции для предотвращения дублирования и потери читаемости.
function logDate(selectedDates, dateStr) {
console.log("Дата:", dateStr);
}
function validateDate(selectedDates) {
if (!selectedDates.length) {
console.warn("Дата не выбрана");
}
}
flatpickr("#input", {
onChange: [logDate, validateDate]
});
Такой подход позволяет повторно использовать обработчики в различных конфигурациях без изменения их внутренней логики.
Удаление конкретного обработчика требует явного управления массивом функций. Поскольку библиотека не индексирует функции по именам автоматически, удаление выполняется через фильтрацию.
fp.set("onClose", fp.config.onClose.filter(fn => fn !== validateDate));
Замена поведения достигается пересборкой массива, что обеспечивает полный контроль над цепочкой вызовов.
При множественных обработчиках критично избегать взаимного влияния через общие переменные. Каждый обработчик должен опираться на аргументы события и состояние экземпляра, а не на внешние изменяемые данные.
let sharedState = 0;
onOpen: [
function() {
sharedState++;
},
function() {
console.log(sharedState);
}
]
Подобные конструкции формируют скрытые зависимости и усложняют прогнозирование поведения системы.
В ситуациях, когда требуется различная приоритетность выполнения, порядок в массиве становится основным инструментом управления.
onChange: [
highPriorityHandler,
mediumPriorityHandler,
lowPriorityHandler
]
Отсутствие встроенной системы приоритетов компенсируется структурированием массива и явным разделением ответственности между обработчиками.
Один и тот же обработчик может быть подключён к нескольким событиям, обеспечивая согласованную реакцию на разные изменения состояния календаря.
function syncState(selectedDates, dateStr, instance) {
console.log("Синхронизация состояния");
}
flatpickr("#input", {
onOpen: [syncState],
onChange: [syncState],
onClose: [syncState]
});
Такая модель формирует единый слой реактивности, упрощающий поддержку общей логики.