Обработчик onDraw

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

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

Сигнатура и контекст выполнения

Обработчик задаётся в конфигурации экземпляра календаря:

new Pikaday({
    field: document.getElementById('date'),
    onDraw: function() {
        // логика при отрисовке календаря
    }
});

Функция вызывается в контексте экземпляра календаря, то есть this внутри обработчика указывает на текущий объект пикера.

Через this доступны основные свойства и методы:

  • this.el — корневой DOM-элемент календаря
  • this.currentMonth — текущий отображаемый месяц
  • this.currentYear — текущий отображаемый год
  • this.getDate() — выбранная дата (если есть)
  • this.config — объект конфигурации

Контекст важен, так как onDraw часто используется для динамического изменения внешнего вида уже построенной DOM-структуры.

Жизненный цикл и причины вызова

onDraw срабатывает в следующих случаях:

  • инициализация календаря
  • переключение месяца (стрелки навигации)
  • программное изменение месяца через API
  • изменение даты, влияющее на доступность дней
  • пересчёт ограничений minDate и maxDate
  • обновление состояния после внешних изменений конфигурации

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

Работа с DOM внутри onDraw

Так как рендер календаря в Pikaday является динамическим, любые прямые изменения DOM должны выполняться внутри onDraw, иначе они будут потеряны при следующей перерисовке.

Типичный пример: добавление кастомных классов к ячейкам дней.

new Pikaday({
    field: document.getElementById('date'),
    onDraw: function() {
        const cells = this.el.querySelectorAll('.pika-day');

        cells.forEach(cell => {
            const day = parseInt(cell.textContent, 10);

            if (day === 1) {
                cell.classList.add('is-first-day');
            }

            if (day % 2 === 0) {
                cell.classList.add('is-even');
            }
        });
    }
});

Любая логика стилизации должна быть идемпотентной: повторный вызов onDraw не должен накапливать состояния без очистки.

Особенности повторного рендера

Каждый вызов onDraw фактически означает новый DOM-слой для сетки календаря. Это приводит к важному правилу: изменения, внесённые вне onDraw, не сохраняются.

Пример неправильного подхода:

const picker = new Pikaday({ field: input });

const cells = document.querySelectorAll('.pika-day');
cells.forEach(c => c.classList.add('highlight'));

Такой код неустойчив, потому что при следующей отрисовке все изменения исчезнут.

Корректный подход — всегда работать внутри onDraw.

Взаимодействие с ограничениями дат

При использовании minDate и maxDate происходит автоматическое изменение состояния ячеек календаря. onDraw позволяет расширить эту логику, например, добавляя визуальные маркеры недоступных дат.

new Pikaday({
    field: document.getElementById('date'),
    minDate: new Date(2026, 0, 1),
    maxDate: new Date(2026, 11, 31),
    onDraw: function() {
        const cells = this.el.querySelectorAll('.pika-day');

        cells.forEach(cell => {
            const day = parseInt(cell.textContent, 10);

            if (cell.classList.contains('is-disabled')) {
                cell.setAttribute('title', 'Дата недоступна');
            }
        });
    }
});

Здесь важно учитывать, что библиотека уже добавляет базовые классы состояния, а onDraw лишь дополняет их.

Состояние и вычисления внутри обработчика

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

const holidays = [1, 2, 3];

new Pikaday({
    field: document.getElementById('date'),
    onDraw: function() {
        const cells = this.el.querySelectorAll('.pika-day');

        cells.forEach(cell => {
            const day = parseInt(cell.textContent, 10);

            if (holidays.includes(day)) {
                cell.classList.add('is-holiday');
            }
        });
    }
});

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

Работа с производительностью

Поскольку onDraw вызывается часто, любые тяжёлые операции внутри него влияют на отзывчивость интерфейса.

Критические моменты:

  • повторные запросы к API внутри onDraw недопустимы
  • сложные вычисления должны кэшироваться
  • DOM-запросы следует минимизировать
  • желательно использовать локальные переменные для this.el.querySelectorAll

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

new Pikaday({
    field: input,
    onDraw: function() {
        const el = this.el;
        const cells = el.querySelectorAll('.pika-day');

        for (let i = 0; i < cells.length; i++) {
            const cell = cells[i];
            const value = +cell.textContent;

            if (value > 25) {
                cell.classList.add('is-late-month');
            }
        }
    }
});

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

При расширении интерфейса календаря через стили или сторонние скрипты onDraw становится точкой синхронизации состояния DOM.

Особенно это важно при:

  • добавлении иконок событий
  • подсветке диапазонов дат
  • интеграции с внешними календарями
  • динамическом обновлении локализации
new Pikaday({
    field: input,
    onDraw: function() {
        const days = this.el.querySelectorAll('.pika-day');

        days.forEach(d => {
            if (d.dataset.event === 'true') {
                d.innerHTML += '<span class="dot"></span>';
            }
        });
    }
});

Любое изменение innerHTML требует осторожности, так как может конфликтовать с внутренней разметкой Pikaday.

Типичные ошибки при использовании onDraw

Часто встречающиеся проблемы:

  • накопление классов при повторных вызовах без очистки
  • попытка использовать DOM до завершения отрисовки
  • выполнение тяжёлой логики внутри обработчика
  • изменение структуры календаря вместо добавления слоёв поверх
  • обращение к несуществующим элементам при смене месяца

Правильная стратегия — считать onDraw чистой точкой синхронизации, а не местом построения логики приложения.

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

onDraw тесно связан с внутренними методами обновления состояния. Любое изменение месяца, года или диапазона приводит к полной перерисовке, и, соответственно, повторному вызову обработчика.

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