Параметр onDraw

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

Основная задача onDraw заключается в пост-обработке отрисованного календаря: добавлении классов, динамической модификации элементов, внедрении кастомного поведения и синхронизации UI с внешним состоянием приложения.


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

onDraw: function() { }

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

  • this внутри onDraw указывает на экземпляр Pikaday
  • доступ к внутренним свойствам календаря осуществляется через this.el, this._o, this._current, this._renderState

Пример доступа к текущему месяцу:

onDraw: function() {
    console.log(this.getMoment().format('YYYY-MM'));
}

Момент вызова onDraw в жизненном цикле

Callback вызывается после следующих событий:

  • первичная инициализация календаря
  • смена месяца (переключение стрелками)
  • изменение года
  • обновление состояния после программного вызова методов навигации
  • повторный рендер при изменении конфигурации

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


Контекст DOM на момент вызова

На момент выполнения callback:

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

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


Типичные задачи onDraw

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

Одна из самых распространённых задач — динамическая подсветка дат:

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

        days.forEach(function(day) {
            var date = day.getAttribute('data-pika-day');

            if (date === '15') {
                day.classList.add('special-day');
            }
        });
    }
});

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

onDraw: function() {
    var days = this.el.querySelectorAll('.pika-day');

    days.forEach((day) => {
        var dayNum = parseInt(day.textContent, 10);

        if (dayNum % 7 === 0 || dayNum % 7 === 6) {
            day.classList.add('weekend-highlight');
        }
    });
}

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

onDraw: function() {
    var disabledDays = [5, 10, 20];

    this.el.querySelectorAll('.pika-day').forEach(function(day) {
        var value = parseInt(day.textContent, 10);

        if (disabledDays.indexOf(value) !== -1) {
            day.classList.add('is-disabled');
        }
    });
}

Работа с внутренними состояниями экземпляра

Экземпляр Pikaday предоставляет доступ к текущему отображаемому месяцу:

  • this.getDate() — выбранная дата
  • this.getMoment() — текущая дата через Moment.js (если используется)
  • this.gotoMonth() — переход к месяцу
  • this.gotoYear() — переход к году

Пример использования текущего состояния:

onDraw: function() {
    var currentMonth = this.getMoment().month();

    if (currentMonth === 11) {
        this.el.classList.add('december-theme');
    } else {
        this.el.classList.remove('december-theme');
    }
}

Манипуляции с DOM внутри onDraw

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

  • идемпотентными
  • устойчивыми к повторному применению
  • не зависящими от предыдущего состояния

Пример безопасного паттерна:

onDraw: function() {
    var root = this.el;

    root.querySelectorAll('.custom-marker').forEach(function(el) {
        el.remove();
    });

    var today = new Date().getDate();

    root.querySelectorAll('.pika-day').forEach(function(day) {
        if (parseInt(day.textContent, 10) === today) {
            var marker = document.createElement('span');
            marker.className = 'custom-marker';
            day.appendChild(marker);
        }
    });
}

Взаимодействие с другими callback-функциями

onOpen vs onDraw

  • onOpen вызывается при открытии календаря
  • onDraw вызывается при каждом рендере

onDraw может срабатывать многократно после onOpen.


onSelect vs onDraw

  • onSelect реагирует на выбор даты
  • onDraw реагирует на перерисовку интерфейса

Выбор даты может триггерить перерисовку, что косвенно вызывает onDraw.


onChange и влияние на рендер

Изменение состояния поля ввода может привести к:

  • пересозданию UI
  • обновлению месяца
  • повторному вызову onDraw

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

Ключевая особенность onDraw заключается в высокой частоте вызова:

  • каждый переход по месяцам
  • любое программное обновление календаря
  • повторная инициализация состояния

Это накладывает ограничения:

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

Оптимизация работы внутри onDraw

Для снижения нагрузки используются следующие подходы:

Кэширование DOM-узлов

onDraw: function() {
    if (!this._cachedDays) {
        this._cachedDays = this.el.querySelectorAll('.pika-day');
    }

    this._cachedDays.forEach(function(day) {
        day.classList.add('optimized');
    });
}

Минимизация перерасчётов

onDraw: function() {
    if (this._lastRenderedMonth === this.getMoment().month()) {
        return;
    }

    this._lastRenderedMonth = this.getMoment().month();
}

Влияние CSS и архитектуры DOM

Использование onDraw тесно связано с CSS-моделью:

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

Пример структуры классов:

.special-day {
    background-color: #ffe08a;
    border-radius: 4px;
}

.weekend-highlight {
    color: #d33;
}

Расширенные сценарии использования

Маркировка событий из внешнего источника

var events = {
    12: 'meeting',
    18: 'holiday',
    25: 'deadline'
};

onDraw: function() {
    this.el.querySelectorAll('.pika-day').forEach((day) => {
        var d = parseInt(day.textContent, 10);

        if (events[d]) {
            day.setAttribute('data-event', events[d]);
            day.classList.add('has-event');
        }
    });
}

Синхронизация с внешним состоянием приложения

onDraw: function() {
    var selected = this.getDate();

    if (selected) {
        document.body.setAttribute('data-selected-date', selected.toISOString());
    }
}

Поведенческие ограничения

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