Экземпляр календаря Pikaday представляет собой объект, связанный с DOM-элементом input и внутренним состоянием выбора даты, рендеринга календарной сетки и обработчиков событий. При классическом использовании создаётся один экземпляр на один input:
const picker = new Pikaday({
field: document.querySelector('#date'),
format: 'YYYY-MM-DD'
});
Такой подход прост, но в реальных приложениях — особенно в SPA — приводит к избыточному числу экземпляров, дублированию логики и росту потребления памяти. Переиспользование экземпляра становится ключевой стратегией оптимизации.
Переиспользование заключается в том, что один и тот же объект Pikaday используется для нескольких input-полей или для одного поля, но с изменяющимися параметрами состояния (дата, минимальные/максимальные границы, позиционирование).
Ключевая особенность: Pikaday не «привязан навсегда» к одному input.
Поле field можно переназначать, а состояние календаря —
обновлять.
Одним из базовых механизмов переиспользования является динамическая
замена field.
const picker = new Pikaday({
field: document.querySelector('#input-1'),
format: 'YYYY-MM-DD'
});
// позже в коде
picker.config({
field: document.querySelector('#input-2')
});
Однако в чистом Pikaday прямой метод config() не всегда
используется для смены поля в зависимости от версии. Более надёжный
подход — ручное отвязывание и привязка:
picker._o.field = document.querySelector('#input-2');
picker.hide();
При таком подходе важно учитывать, что внутренние обработчики кликов и фокуса уже привязаны к старому DOM-элементу.
В ситуациях, где DOM-узлы часто меняются, предпочтительно явное уничтожение экземпляра:
picker.destroy();
После уничтожения создаётся новый экземпляр, но логика может оставаться централизованной:
function createPicker(field) {
return new Pikaday({
field,
format: 'YYYY-MM-DD'
});
}
Такой подход формально не переиспользует объект, но переиспользует фабрику и снижает сложность управления состоянием.
Более эффективная модель в SPA — один глобальный экземпляр календаря, который «прикрепляется» к разным input при фокусе.
const picker = new Pikaday({
field: document.createElement('input'),
onSelect: function(date) {
if (this._currentField) {
this._currentField.value = this.toString();
}
}
});
Далее при фокусе:
document.addEventListener('focusin', (e) => {
if (e.target.matches('.date-input')) {
picker._currentField = e.target;
picker._o.field = e.target;
picker.setDate(e.target.value || new Date());
picker.show();
}
});
Такой паттерн позволяет:
При повторном использовании критически важно синхронизировать внутреннюю дату календаря с новым контекстом.
picker.setDate('2026-06-01', true);
Второй параметр true предотвращает триггер лишних
событий, что важно при программной смене поля.
picker.setDate(null);
Это предотвращает «протекание» состояния между разными input.
Метод gotoDate влияет только на отображаемый месяц, не
меняя выбранную дату.
picker.gotoDate(new Date());
При переиспользовании это позволяет разъединить:
Это особенно полезно при переключении между полями с разными диапазонами дат.
Основной риск — неочищенные обработчики событий.
Pikaday внутри создаёт:
documentЕсли экземпляр просто «перекидывать» между полями без очистки старых связей, возникают:
Календарь начинает вызывать onSelect несколько раз за одно действие.
В сложных интерфейсах часто применяется единый экземпляр:
const DatePickerSingleton = (function () {
let instance;
function create() {
return new Pikaday({
field: document.createElement('input')
});
}
return {
getInstance() {
if (!instance) {
instance = create();
}
return instance;
}
};
})();
Использование:
const picker = DatePickerSingleton.getInstance();
Далее логика переключения поля управляется отдельно.
В формах, где поля создаются и удаляются динамически, переиспользование снижает накладные расходы.
Пример структуры:
function attachDateInputs(container) {
const picker = window.sharedPicker;
container.querySelectorAll('.date-input').forEach(input => {
input.addEventListener('focus', () => {
picker._o.field = input;
picker.setDate(input.value || new Date(), true);
picker.show();
});
});
}
Такой подход исключает необходимость создавать календарь для каждого нового элемента.
В SPA (React, Vue, Angular) ключевая проблема — повторный рендер компонентов.
useEffect(() => {
const picker = new Pikaday({ field: ref.current });
return () => picker.destroy();
}, []);
При этом при каждом монтировании создаётся новый экземпляр.
Использование глобального экземпляра:
Переиспользование часто включает изменение конфигурации:
picker.setMinDate(new Date('2026-01-01'));
picker.setMaxDate(new Date('2026-12-31'));
Хотя Pikaday не всегда предоставляет явные методы для всех параметров, многие ограничения можно менять через внутренний объект конфигурации:
picker._o.minDate = new Date('2026-01-01');
picker._o.maxDate = new Date('2026-12-31');
picker.draw();
Метод hide() является ключевым элементом
переиспользования:
picker.hide();
Вместо пересоздания календарь просто скрывается и позже переиспользуется.
Это важно для:
Наиболее устойчивый подход объединяет несколько стратегий:
fieldsetDatedestroyfunction bindPickerToField(picker, field) {
picker._o.field = field;
picker.setDate(field.value || null, true);
picker.hide();
}
При container-режиме переиспользование усложняется, так
как календарь встроен в DOM:
new Pikaday({
field: input,
bound: false,
container: document.querySelector('#calendar')
});
Здесь переиспользование чаще сводится к:
draw()fieldОсобое внимание требуется обработчику onSelect:
onSelect: function(date) {
if (this._o.field) {
this._o.field.value = this.toString();
}
}
При переиспользовании важно, чтобы this._o.field всегда
указывал на актуальный input, иначе данные будут записываться в
устаревший элемент.
При правильной организации один экземпляр Pikaday перестаёт быть «виджетом для input» и становится централизованным сервисом выбора даты:
Такой подход особенно эффективен в интерфейсах с большим количеством форм, таблиц и динамических элементов ввода даты.