Flatpickr используется в сценариях, где календарные поля создаются и уничтожаются во время работы интерфейса: модальные окна, динамические формы, таблицы с редактируемыми строками, компоненты SPA. В таких условиях ключевым становится управление жизненным циклом экземпляров, а также корректная очистка ресурсов.
Базовая модель работы строится вокруг создания экземпляра через
flatpickr(element, options). При статической разметке
элемент уже присутствует в DOM, однако в динамических интерфейсах
элемент может появляться после рендера.
import flatpickr from "flatpickr";
function attachDatepicker(input) {
return flatpickr(input, {
dateFormat: "Y-m-d",
allowInput: true
});
}
При создании элементов на лету важно учитывать момент инициализации: экземпляр должен создаваться только после вставки узла в DOM.
const container = document.querySelector("#container");
function addInput() {
const input = document.createElement("input");
input.type = "text";
input.className = "date-field";
container.appendChild(input);
const fp = flatpickr(input, {
enableTime: true
});
return fp;
}
Каждый вызов создаёт независимый экземпляр с собственной конфигурацией и состоянием.
При динамическом создании требуется сохранение ссылок, иначе
управление жизненным циклом становится невозможным. Обычно используется
Map или массив.
const instances = new Map();
function createField(id) {
const input = document.createElement("input");
input.dataset.id = id;
document.body.appendChild(input);
const instance = flatpickr(input, {
defaultDate: new Date()
});
instances.set(id, instance);
}
Такой подход позволяет обращаться к конкретному календарю без поиска по DOM.
Удаление поля без уничтожения экземпляра приводит к утечкам памяти и
сохранению обработчиков событий. Для корректного удаления используется
метод destroy().
function removeField(id) {
const instance = instances.get(id);
if (instance) {
instance.destroy();
instances.delete(id);
}
}
Метод destroy() выполняет:
После вызова объект становится невалидным и не должен использоваться.
В динамических интерфейсах часто требуется пересоздание календаря при изменении структуры формы. Например, при переключении режима редактирования.
function reinit(input, config) {
if (input._flatpickr) {
input._flatpickr.destroy();
}
return flatpickr(input, config);
}
Внутреннее свойство _flatpickr позволяет получить доступ
к привязанному экземпляру без внешнего хранилища.
В некоторых сценариях календарь создаётся только при взаимодействии пользователя, например при фокусе.
function lazyInit(input) {
let instance = null;
input.addEventListener("focus", () => {
if (!instance) {
instance = flatpickr(input, {
clickOpens: true
});
}
});
}
Такой подход снижает нагрузку при большом количестве полей.
Модальные окна часто создаются и уничтожаются вместе с содержимым. В таких условиях экземпляры календаря должны уничтожаться при закрытии модального слоя.
let modalInstance = null;
function openModal() {
const input = document.querySelector("#modal-date");
modalInstance = flatpickr(input, {
enableTime: true
});
}
function closeModal() {
if (modalInstance) {
modalInstance.destroy();
modalInstance = null;
}
document.querySelector("#modal").remove();
}
Невыполнение очистки приводит к накоплению обработчиков и повторной инициализации поверх старых экземпляров.
В таблицах с добавлением строк используется делегирование событий или повторная инициализация после вставки строки.
function addRow(table) {
const row = document.createElement("tr");
const cell = document.createElement("td");
const input = document.createElement("input");
cell.appendChild(input);
row.appendChild(cell);
table.appendChild(row);
flatpickr(input, {
dateFormat: "d.m.Y"
});
}
При удалении строки важно синхронно уничтожать связанный экземпляр.
function removeRow(row) {
const input = row.querySelector("input");
if (input && input._flatpickr) {
input._flatpickr.destroy();
}
row.remove();
}
Flatpickr не предоставляет полноценного deep-update конфигурации для всех параметров, поэтому распространённой практикой является пересоздание экземпляра.
function updateConfig(input, newConfig) {
const old = input._flatpickr;
const selectedDates = old ? old.selectedDates : [];
if (old) {
old.destroy();
}
const instance = flatpickr(input, {
...newConfig,
defaultDate: selectedDates
});
return instance;
}
Сохранение состояния перед уничтожением позволяет избежать потери пользовательского ввода.
При большом количестве динамических полей используется централизованный менеджер экземпляров.
class DatepickerPool {
constructor() {
this.pool = new WeakMap();
}
create(input, config) {
const instance = flatpickr(input, config);
this.pool.set(input, instance);
return instance;
}
destroy(input) {
const instance = this.pool.get(input);
if (instance) {
instance.destroy();
this.pool.delete(input);
}
}
destroyAll() {
this.pool.forEach((instance, input) => {
instance.destroy();
});
this.pool = new WeakMap();
}
}
Использование WeakMap позволяет автоматически
освобождать память при удалении DOM-узлов сборщиком мусора.
В некоторых интерфейсах параметры передаются через
data-* атрибуты. При их изменении требуется пересоздание
календаря.
function observeInput(input) {
const observer = new MutationObserver(() => {
const mode = input.dataset.mode;
if (input._flatpickr) {
input._flatpickr.destroy();
}
flatpickr(input, {
mode
});
});
observer.observe(input, { attributes: true });
}
Такой подход характерен для систем с декларативной конфигурацией.
При удалении контейнера с несколькими календарями требуется проход по всем дочерним элементам.
function destroyContainer(container) {
const inputs = container.querySelectorAll("input");
inputs.forEach(input => {
if (input._flatpickr) {
input._flatpickr.destroy();
}
});
container.remove();
}
Игнорирование этого шага приводит к сохранению подписок на события даже после удаления узлов из DOM.
В SPA часто данные приходят асинхронно, после чего создаются поля.
async function renderForm() {
const data = await fetch("/api/form").then(r => r.json());
data.fields.forEach(field => {
const input = document.createElement("input");
input.value = field.value;
document.body.appendChild(input);
flatpickr(input, {
defaultDate: field.date
});
});
}
Инициализация выполняется строго после завершения рендера DOM-структуры.
Одной из частых ошибок является повторное создание экземпляра на одном и том же элементе. Для предотвращения используется проверка:
function safeInit(input, config) {
if (input._flatpickr) {
return input._flatpickr;
}
return flatpickr(input, config);
}
Такой контроль предотвращает дублирование обработчиков и конфликт состояния.
При смене маршрута в SPA или перерисовке формы целиком выполняется полный цикл уничтожения и повторного создания.
function rebuildUI(container, config) {
container.querySelectorAll("input").forEach(input => {
if (input._flatpickr) {
input._flatpickr.destroy();
}
});
container.innerHTML = "";
for (let i = 0; i < 5; i++) {
const input = document.createElement("input");
container.appendChild(input);
flatpickr(input, config);
}
}
Такая стратегия обеспечивает предсказуемое состояние интерфейса и отсутствие «висящих» экземпляров.