Добавление произвольного контента

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

Ключевая особенность подхода — работа с уже отрендеренными элементами через API событий и callback-функции.


Архитектура DOM-структуры календаря

После инициализации Flatpickr создаёт контейнер с классом .flatpickr-calendar, внутри которого формируется:

  • заголовок (месяц, навигация)
  • сетка дней
  • дополнительные панели (в зависимости от режима)
  • служебные элементы управления

Каждый день месяца представлен элементом .flatpickr-day, который создаётся динамически при каждом рендере.

Именно этот слой становится точкой расширения для вставки пользовательского контента.


Хук onDayCreate как основной механизм расширения ячеек

Наиболее прямой способ добавить произвольный контент в ячейки календаря — использование onDayCreate. Этот callback вызывается при создании каждого DOM-элемента дня.

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

    if (date.getDay() === 1) {
      const badge = document.createElement("span");
      badge.className = "custom-badge";
      badge.textContent = "M";

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

В данном контексте dayElem — это уже готовый DOM-узел, в который можно добавлять любые дочерние элементы:

  • бейджи
  • иконки
  • индикаторы событий
  • числовые маркеры

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


Добавление событийных маркеров в ячейки

Распространённый сценарий — отображение событий внутри конкретных дат.

const events = {
  "2026-06-03": ["meeting", "deploy"],
  "2026-06-10": ["vacation"]
};

flatpickr("#calendar", {
  onDayCreate: function(_, __, fp, dayElem) {
    const iso = fp.formatDate(dayElem.dateObj, "Y-m-d");

    if (events[iso]) {
      const container = document.createElement("div");
      container.className = "event-dots";

      events[iso].forEach(() => {
        const dot = document.createElement("span");
        dot.className = "event-dot";
        container.appendChild(dot);
      });

      dayElem.appendChild(container);
    }
  }
});

Такой подход превращает календарь в слой визуализации данных, где DOM становится носителем метаинформации.


Использование onReady для модификации структуры календаря

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

Примеры задач:

  • добавление пользовательского футера
  • вставка легенды
  • модификация заголовка
  • добавление панели действий
flatpickr("#calendar", {
  onReady: function(selectedDates, dateStr, fp) {
    const footer = document.createElement("div");
    footer.className = "fp-footer";
    footer.textContent = "Статус: синхронизировано";

    fp.calendarContainer.appendChild(footer);
  }
});

fp.calendarContainer — корневой DOM-узел календаря, через который можно расширять структуру без вмешательства в внутреннюю разметку.


Динамическая вставка HTML в заголовок календаря

Flatpickr не предоставляет официального шаблонизатора заголовка, однако его можно модифицировать через prevMonthNav и nextMonthNav, а также через прямой доступ к .cur-month.

flatpickr("#calendar", {
  onReady: function(_, __, fp) {
    const title = fp.monthNav.querySelector(".cur-month");

    const indicator = document.createElement("span");
    indicator.className = "sync-indicator";
    indicator.textContent = "●";

    title.appendChild(indicator);
  }
});

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

  • индикаторов синхронизации
  • статусов загрузки
  • кастомных иконок рядом с месяцем

Инъекция сложного контента через переотрисовку

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

Подход основан на хранении состояния вне DOM:

const notes = {
  "2026-06-15": "Deadline",
  "2026-06-20": "Review"
};

flatpickr("#calendar", {
  onDayCreate: function(_, __, fp, dayElem) {
    const key = fp.formatDate(dayElem.dateObj, "Y-m-d");

    if (notes[key]) {
      const note = document.createElement("div");
      note.className = "day-note";
      note.textContent = notes[key];

      dayElem.appendChild(note);
    }
  }
});

DOM рассматривается как производное состояние от внешней модели данных.


Использование hook-цепочек для сложной логики

Flatpickr поддерживает несколько событий, которые можно комбинировать:

  • onMonthChange
  • onYearChange
  • onValueUpdate
  • onOpen
  • onClose

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

flatpickr("#calendar", {
  onMonthChange: function(selectedDates, dateStr, fp) {
    fp.redraw();
  },

  onDayCreate: function(_, __, fp, dayElem) {
    const date = dayElem.dateObj;

    if (date.getDate() === 1) {
      const label = document.createElement("span");
      label.textContent = "Start";
      label.className = "month-start-label";

      dayElem.appendChild(label);
    }
  }
});

Кастомизация через внешние контейнеры

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

Flatpickr позволяет использовать wrap: true, что даёт доступ к обёртке:

flatpickr("#wrapper", {
  wrap: true,
  onReady: function(_, __, fp) {
    const overlay = document.createElement("div");
    overlay.className = "calendar-overlay";

    fp.calendarContainer.appendChild(overlay);
  }
});

Overlay-слои применяются для:

  • heatmap-индикаторов
  • графических сеток
  • визуализации плотности событий

Управление перерисовкой при изменении данных

Добавление произвольного контента тесно связано с ручным контролем обновления UI.

Метод redraw() используется для полной перегенерации DOM:

fp.setDate("2026-06-10");
fp.redraw();

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

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

Безопасная работа с DOM-узлами Flatpickr

Прямое вмешательство в DOM требует соблюдения ограничений:

  • нельзя полагаться на стабильность внутренней структуры
  • нельзя кэшировать .flatpickr-day между рендерами
  • нельзя модифицировать системные классы без необходимости

Предпочтительный подход — добавление собственных классов и контейнеров:

dayElem.classList.add("has-custom-content");

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


Расширение через пользовательские плагины

Для сложных сценариев добавления контента используется обёртка над Flatpickr как плагин:

function customContentPlugin(fp) {
  return {
    onDayCreate(_, __, instance, dayElem) {
      const marker = document.createElement("i");
      marker.className = "plugin-marker";
      dayElem.appendChild(marker);
    }
  };
}

flatpickr("#calendar", {
  plugins: [customContentPlugin]
});

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


Встраивание интерактивных элементов

Произвольный контент может включать интерактивные элементы:

  • кнопки
  • чекбоксы
  • всплывающие подсказки
  • мини-формы
dayElem.addEventListener("click", (e) => {
  if (e.target.classList.contains("quick-action")) {
    console.log("Action triggered");
  }
});

Важно учитывать, что Flatpickr обрабатывает клики по дням, поэтому вложенные элементы должны предотвращать конфликт событий при необходимости через stopPropagation().


Итоговая модель расширения контента

Добавление произвольного контента в Flatpickr опирается на три базовых слоя:

  • onDayCreate — модификация ячеек календаря
  • onReady — модификация глобальной структуры
  • plugins — инкапсуляция сложной логики

Эти механизмы формируют единый паттерн расширения, в котором DOM остаётся управляемым, а данные — внешними и независимыми от рендера.