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

Flatpickr предоставляет точку расширения для управления тем, как отображаются отдельные дни в календарной сетке. Основной инструмент кастомизации — хук onDayCreate, который вызывается при создании каждой ячейки дня в DOM.

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

Хук onDayCreate и его сигнатура

onDayCreate вызывается для каждой даты, отображаемой в текущем месяце (а также соседних, если включён showOtherMonths).

Сигнатура:

onDayCreate: function(dObj, dStr, fp, dayElem) {
}

Где:

  • dObj — объект Date, соответствующий конкретной ячейке
  • dStr — строковое представление даты в текущем формате
  • fp — экземпляр Flatpickr
  • dayElem — DOM-элемент ячейки дня

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

Базовые операции кастомизации дня

Наиболее распространённые операции внутри onDayCreate:

  • добавление CSS-классов
  • установка data-атрибутов
  • изменение innerHTML
  • вставка дополнительных элементов (badge, icon, marker)
  • визуальная маркировка состояния даты

Пример добавления класса:

onDayCreate: function(dObj, dStr, fp, dayElem) {
  if (dObj.getDay() === 0 || dObj.getDay() === 6) {
    dayElem.classList.add("is-weekend");
  }
}

Условная стилизация дат

Логика кастомного рендеринга чаще всего основана на сравнении даты с внешними данными: событиями, расписанием, ограничениями.

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

const holidays = [
  "2026-01-01",
  "2026-05-09",
  "2026-12-31"
];

function formatDate(date) {
  const y = date.getFullYear();
  const m = String(date.getMonth() + 1).padStart(2, "0");
  const d = String(date.getDate()).padStart(2, "0");
  return `${y}-${m}-${d}`;
}

flatpickr("#calendar", {
  onDayCreate: function(dObj, dStr, fp, dayElem) {
    const dateKey = formatDate(dObj);

    if (holidays.includes(dateKey)) {
      dayElem.classList.add("is-holiday");
    }
  }
});

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

Встраивание индикаторов событий

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

Пример с бейджем:

const eventsMap = {
  "2026-06-01": 3,
  "2026-06-02": 1,
  "2026-06-03": 7
};

flatpickr("#calendar", {
  onDayCreate: function(dObj, dStr, fp, dayElem) {
    const key = dStr;

    if (eventsMap[key]) {
      const badge = document.createElement("span");
      badge.className = "day-badge";
      badge.textContent = eventsMap[key];

      dayElem.appendChild(badge);
    }
  }
});

Здесь важно учитывать, что dayElem уже содержит текстовую ноду с числом дня, и добавление дополнительных элементов может потребовать CSS-позиционирования (position: relative у контейнера).

Полная замена содержимого ячейки

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

onDayCreate: function(dObj, dStr, fp, dayElem) {
  const dayNumber = dObj.getDate();

  dayElem.innerHTML = "";

  const wrapper = document.createElement("div");
  wrapper.className = "custom-day";

  const number = document.createElement("span");
  number.className = "day-number";
  number.textContent = dayNumber;

  const marker = document.createElement("span");
  marker.className = "day-marker";

  wrapper.appendChild(number);
  wrapper.appendChild(marker);
  dayElem.appendChild(wrapper);
}

Такой подход даёт максимальную гибкость, но требует полного контроля над стилями и состояниями (selected, today, disabled).

Работа с состояниями Flatpickr

Flatpickr применяет к ячейкам стандартные классы:

  • flatpickr-day
  • today
  • selected
  • disabled
  • prevMonthDay / nextMonthDay

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

Пример условного расширения состояния:

onDayCreate: function(dObj, dStr, fp, dayElem) {
  if (dayElem.classList.contains("disabled")) {
    dayElem.classList.add("is-blocked");
  }
}

Интеграция с внешними API и динамическими данными

Часто данные для рендеринга приходят асинхронно. В этом случае используется предварительная загрузка данных и кэширование.

let availabilityCache = {};

fetch("/api/availability")
  .then(r => r.json())
  .then(data => {
    availabilityCache = data;
  });

flatpickr("#calendar", {
  onDayCreate: function(dObj, dStr, fp, dayElem) {
    const status = availabilityCache[dStr];

    if (status === "full") {
      dayElem.classList.add("is-full");
    } else if (status === "low") {
      dayElem.classList.add("is-low");
    }
  }
});

Ключевой момент — onDayCreate не ожидает завершения async-операций, поэтому данные должны быть заранее доступны.

Добавление tooltip и вспомогательных атрибутов

Дополнительные метаданные удобно хранить в data-* атрибутах:

onDayCreate: function(dObj, dStr, fp, dayElem) {
  const tooltipText = `Дата: ${dStr}`;

  dayElem.setAttribute("data-tooltip", tooltipText);
}

Или интеграция с кастомным tooltip-движком:

dayElem.addEventListener("mouseenter", () => {
  showTooltip(dayElem, "Дополнительная информация");
});

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

Ограничения и особенности DOM-манипуляций

При использовании пользовательского рендеринга необходимо учитывать внутреннюю переработку DOM при смене месяца. Flatpickr полностью пересоздаёт сетку дней при навигации, поэтому:

  • все изменения применяются заново
  • нельзя рассчитывать на сохранение динамически добавленных DOM-состояний
  • обработчики должны быть идемпотентными

Пример корректного подхода:

onDayCreate: function(dObj, dStr, fp, dayElem) {
  dayElem.classList.remove("custom-state");

  if (dObj.getDate() === 15) {
    dayElem.classList.add("custom-state");
  }
}

Оптимизация производительности рендеринга

При большом количестве логики в onDayCreate возможны просадки производительности. Основные рекомендации:

  • минимизировать DOM-операции
  • избегать тяжёлых вычислений внутри хука
  • использовать предварительно рассчитанные структуры данных (Map, Set)
  • исключать повторное создание одинаковых элементов

Оптимизированный вариант проверки:

const specialDays = new Set(["2026-06-10", "2026-06-11"]);

onDayCreate: function(dObj, dStr, fp, dayElem) {
  if (specialDays.has(dStr)) {
    dayElem.classList.add("is-special");
  }
}

Комбинирование кастомного рендеринга с выбором дат

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

Если полностью заменяется структура DOM, необходимо сохранить возможность клика:

wrapper.addEventListener("click", () => {
  fp.setDate(dObj);
});

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

Использование CSS как альтернативы рендерингу

В некоторых случаях лучше ограничиться CSS-классами, а не модификацией DOM:

onDayCreate: function(dObj, dStr, fp, dayElem) {
  if (dObj.getMonth() !== fp.currentMonth) {
    dayElem.classList.add("dimmed");
  }
}

И затем стилизация:

.dimmed {
  opacity: 0.4;
}

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

Сочетание нескольких источников данных в рендеринге

При сложной логике часто объединяются разные типы информации: праздники, события, ограничения, доступность.

onDayCreate: function(dObj, dStr, fp, dayElem) {
  const key = dStr;

  if (holidaysSet.has(key)) {
    dayElem.classList.add("holiday");
  }

  if (eventsMap[key]) {
    const marker = document.createElement("i");
    marker.className = "event-dot";
    dayElem.appendChild(marker);
  }

  if (blockedSet.has(key)) {
    dayElem.classList.add("blocked");
  }
}

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