Календарь событий

Flatpickr строится вокруг идеи лёгкого и расширяемого календаря, который можно привязать к любому input-элементу и превратить его в полноценный инструмент выбора дат. В контексте событийного календаря ключевая задача — не только выбор даты, но и реакция на действия пользователя, а также визуальное и логическое связывание дат с данными событий.

Базовая инициализация выполняется через вызов flatpickr, которому передаётся DOM-элемент и конфигурация:

flatpickr("#calendar", {
  enableTime: true,
  dateFormat: "Y-m-d H:i",
});

В сценарии событийного календаря конфигурация расширяется за счёт обработки событий и динамической модификации дат:

flatpickr("#calendar", {
  enableTime: true,
  dateFormat: "Y-m-d H:i",
  onChange: function(selectedDates, dateStr, instance) {
    console.log("Выбрана дата:", dateStr);
  }
});

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


Событийная модель Flatpickr

Flatpickr предоставляет набор хуков, которые позволяют управлять жизненным циклом календаря. Эти события формируют основу для построения календаря событийного типа.

onReady

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

onReady: function(selectedDates, dateStr, instance) {
  loadEvents(instance);
}

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


onChange

Основной обработчик взаимодействия пользователя с календарём. Срабатывает при выборе даты или диапазона.

onChange: function(selectedDates, dateStr, instance) {
  const events = getEventsForDate(selectedDates[0]);
  renderEventPanel(events);
}

Этот хук часто используется для обновления интерфейса событий: списка встреч, задач или бронирований.


onOpen и onClose

Эти события позволяют управлять состоянием интерфейса при открытии и закрытии календаря.

onOpen: function(selectedDates, dateStr, instance) {
  instance.calendarContainer.classList.add("calendar-open");
}
onClose: function(selectedDates, dateStr, instance) {
  instance.calendarContainer.classList.remove("calendar-open");
}

В календарях событий эти хуки часто используются для синхронизации состояния с сервером или локальным хранилищем.


onMonthChange и onYearChange

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

onMonthChange: function(selectedDates, dateStr, instance) {
  preloadEvents(instance.currentYear, instance.currentMonth);
}
onYearChange: function(selectedDates, dateStr, instance) {
  preloadEvents(instance.currentYear);
}

Такой подход позволяет реализовать ленивую загрузку событий и уменьшить нагрузку при больших диапазонах дат.


onDayCreate

Один из наиболее мощных инструментов для построения календаря событий. Позволяет модифицировать каждый день при его создании.

onDayCreate: function(dObj, dStr, fp, dayElem) {
  const date = dayElem.dateObj;
  const events = getEventsForDate(date);

  if (events.length > 0) {
    dayElem.classList.add("has-events");
  }
}

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


Привязка событий к датам

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

Карта событий (object map)

const events = {
  "2026-06-01": [
    { title: "Встреча", type: "meeting" },
    { title: "Дедлайн", type: "deadline" }
  ],
  "2026-06-02": [
    { title: "Созвон", type: "call" }
  ]
};

Такой формат обеспечивает быстрый доступ по ключу даты.


Массив событий

const events = [
  { date: "2026-06-01", title: "Встреча" },
  { date: "2026-06-01", title: "Дедлайн" },
  { date: "2026-06-02", title: "Созвон" }
];

Этот подход удобен при работе с API и фильтрацией, но требует дополнительной обработки.


Визуальное отображение событий

Flatpickr по умолчанию не отображает события, поэтому визуализация реализуется через классы и кастомные элементы.

Маркировка дат

onDayCreate: function(dObj, dStr, fp, dayElem) {
  const key = fp.formatDate(dayElem.dateObj, "Y-m-d");

  if (events[key]) {
    dayElem.classList.add("event-day");
  }
}

CSS:

.event-day {
  position: relative;
}

.event-day::after {
  content: "";
  width: 6px;
  height: 6px;
  background: red;
  border-radius: 50%;
  position: absolute;
  bottom: 4px;
  left: 50%;
  transform: translateX(-50%);
}

Множественные события

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

if (events[key]) {
  dayElem.setAttribute("data-count", events[key].length);
}
.event-day::after {
  content: attr(data-count);
  font-size: 10px;
  position: absolute;
  top: 2px;
  right: 2px;
}

Динамическая загрузка событий

При работе с большими календарями данные не загружаются заранее, а подгружаются по мере навигации.

function preloadEvents(year, month) {
  fetch(`/api/events?year=${year}&month=${month}`)
    .then(res => res.json())
    .then(data => {
      mergeEvents(data);
    });
}

События подгружаются в onMonthChange, что позволяет оптимизировать производительность.


Интерактивные сценарии календаря событий

Бронирование

Календарь используется как интерфейс выбора доступных дат:

flatpickr("#booking", {
  minDate: "today",
  disable: unavailableDates,
  onChange: function(selectedDates) {
    checkAvailability(selectedDates[0]);
  }
});

Недоступные даты блокируются через disable.


Расписание задач

При использовании календаря как планировщика задачи связываются с конкретным временем:

enableTime: true,
time_24hr: true,
onChange: function(dates) {
  scheduleTask(dates[0]);
}

Диапазоны событий

Flatpickr поддерживает выбор диапазона дат:

flatpickr("#range", {
  mode: "range",
  onChange: function(dates) {
    if (dates.length === 2) {
      loadEventsBetween(dates[0], dates[1]);
    }
  }
});

Диапазон часто используется для аналитических календарей и отчетов.


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

Для календаря событий важно синхронизировать состояние между UI и данными.

Обновление после изменения данных

function updateCalendar(instance) {
  instance.redraw();
}

Метод redraw() перерисовывает календарь, применяя новые классы и состояния.


Программный выбор даты

instance.setDate("2026-06-10");

Используется для перехода к дате события из внешнего списка.


Очистка выбора

instance.clear();

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


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

При большом количестве событий основная нагрузка приходится на рендеринг и обработчики onDayCreate.

Оптимизация включает:

  • кэширование событий по месяцам
  • минимизацию DOM-операций
  • предварительную агрегацию данных
  • использование Map вместо объектов при больших наборах данных
const eventMap = new Map();

function setEvents(events) {
  eventMap.clear();
  events.forEach(e => {
    const key = e.date;
    if (!eventMap.has(key)) eventMap.set(key, []);
    eventMap.get(key).push(e);
  });
}

Кастомные сценарии поведения дат

Блокировка по условию

disable: [
  function(date) {
    return date.getDay() === 0;
  }
]

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


Условная доступность

onDayCreate: function(dObj, dStr, fp, dayElem) {
  const date = dayElem.dateObj;
  const isPast = date < new Date();

  if (isPast) {
    dayElem.classList.add("past-date");
  }
}

Связь календаря с внешними интерфейсами

Календарь событий редко существует изолированно. Он синхронизируется со списками, таблицами и карточками.

onChange: function(selectedDates) {
  const list = getEventsForDate(selectedDates[0]);
  renderList(list);
}

При изменении даты обновляется связанный компонент интерфейса, что превращает календарь в центральный элемент навигации по данным.


Расширение логики событий

При необходимости календарь может использоваться как слой отображения поверх сложной бизнес-логики:

  • планирование ресурсов
  • управление бронированиями
  • контроль дедлайнов
  • отображение аналитических периодов

Каждый из этих сценариев требует собственной модели событий, но Flatpickr остаётся точкой взаимодействия с датой и временем, а не хранилищем логики.