Работа с диапазонами дат в интерфейсах календаря требует не только логической реализации выбора, но и продуманной визуальной подсветки состояния каждого дня. В контексте Pikaday основная сложность заключается в том, что библиотека изначально ориентирована на выбор одной даты, поэтому диапазон реализуется через внешнюю логику и последующую стилизацию через CSS-классы.
При реализации диапазона дат используется два состояния:
Дополнительно вычисляется промежуточное состояние:
Логика обычно строится на обработке события onSelect,
где попеременно устанавливаются границы диапазона:
let startDate = null;
let endDate = null;
const picker = new Pikaday({
field: document.getElementById('datepicker'),
onSelect: function(date) {
if (!startDate || (startDate && endDate)) {
startDate = date;
endDate = null;
} else {
if (date < startDate) {
endDate = startDate;
startDate = date;
} else {
endDate = date;
}
}
updateUI();
}
});
С точки зрения стилизации ключевым моментом становится не сама логика, а корректная передача состояния в DOM.
Pikaday позволяет использовать кастомизацию через метод
onDraw, который вызывается при каждом рендере календаря.
Именно здесь происходит маркировка ячеек дат.
onDraw: function() {
const cells = document.querySelectorAll('.pika-button');
cells.forEach(cell => {
const time = cell.getAttribute('data-pika-day');
const date = new Date(time);
cell.classList.remove('is-in-range', 'is-range-start', 'is-range-end');
if (startDate && date.getTime() === startDate.getTime()) {
cell.classList.add('is-range-start');
}
if (endDate && date.getTime() === endDate.getTime()) {
cell.classList.add('is-range-end');
}
if (startDate && endDate && date > startDate && date < endDate) {
cell.classList.add('is-in-range');
}
});
}
Ключевой принцип заключается в том, что библиотека не хранит диапазон как сущность — он полностью формируется внешним кодом.
CSS-стили формируют визуальное восприятие диапазона. Основная задача — создать непрерывную полосу выделения с акцентами на границах.
.is-in-range {
background-color: #e6f2ff;
color: #1a1a1a;
border-radius: 0;
}
Для стартовой и конечной дат используется более выраженное оформление:
.is-range-start,
.is-range-end {
background-color: #1a73e8;
color: #ffffff;
font-weight: 600;
}
Чтобы диапазон выглядел цельным, важно убрать скругления у промежуточных дней:
.is-in-range {
border-radius: 0;
}
Визуальная целостность диапазона достигается за счёт псевдо-логики соединения ячеек. В Pikaday каждая дата — это отдельная кнопка, поэтому эффект “полосы” создаётся исключительно стилями.
Дополнительно используется разделение состояний:
.is-range-start {
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
}
.is-range-end {
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
}
Если диапазон состоит из одной даты, оба класса применяются одновременно, что требует дополнительного контроля:
if (startDate && endDate && startDate.getTime() === endDate.getTime()) {
cell.classList.add('is-range-start', 'is-range-end');
}
Hover-состояния играют ключевую роль при выборе конечной даты. Часто используется временная визуализация “предпросмотра диапазона”.
Добавляется переменная hoverDate, которая обновляется
при наведении:
let hoverDate = null;
document.addEventListener('mouseover', (e) => {
if (e.target.classList.contains('pika-button')) {
hoverDate = new Date(e.target.getAttribute('data-pika-day'));
updateUI();
}
});
И далее учитывается в onDraw:
if (startDate && !endDate && hoverDate) {
const min = startDate < hoverDate ? startDate : hoverDate;
const max = startDate < hoverDate ? hoverDate : startDate;
if (date > min && date < max) {
cell.classList.add('is-in-range-preview');
}
}
CSS для предпросмотра отличается меньшей интенсивностью:
.is-in-range-preview {
background-color: rgba(26, 115, 232, 0.15);
}
При сложной логике диапазонов возникает проблема пересечения классов. Например, дата может одновременно быть:
Для предотвращения визуальных артефактов применяется приоритетность:
is-range-start / is-range-endis-in-rangeis-in-range-previewЭто достигается либо порядком добавления классов, либо более специфичными CSS-селекторами:
.is-range-start.is-in-range-preview {
background-color: #1a73e8;
}
При работе с диапазонами важно учитывать недоступные даты (disabled). Они должны визуально отличаться и не участвовать в логике подсветки.
.is-disabled {
color: #bdbdbd;
pointer-events: none;
background-color: transparent;
}
Если отключённая дата попадает в диапазон логически (например, из-за ошибок вычисления), её состояние должно принудительно переопределяться:
if (cell.classList.contains('is-disabled')) {
cell.classList.remove('is-in-range', 'is-range-start', 'is-range-end');
}
Стилизация диапазона должна учитывать контекст темы интерфейса. В тёмных интерфейсах важнее контраст и прозрачность, чем насыщенность цвета.
.dark-theme .is-in-range {
background-color: rgba(66, 133, 244, 0.25);
color: #ffffff;
}
.dark-theme .is-range-start,
.dark-theme .is-range-end {
background-color: #4285f4;
}
Pikaday рендерит календарь в виде таблицы, где визуальная целостность диапазона зависит от соседних ячеек. Особенно важны переходы между неделями.
Для устранения визуальных разрывов применяется устранение зазоров:
.pika-table td {
padding: 0;
}
.pika-button {
width: 100%;
height: 100%;
}
Дополнительно используется компенсация границ:
.is-in-range + .is-in-range {
border-left: none;
}
При расширении логики до нескольких диапазонов добавляется массив интервалов:
const ranges = [
{ start: new Date(2026, 0, 1), end: new Date(2026, 0, 5) },
{ start: new Date(2026, 0, 10), end: new Date(2026, 0, 15) }
];
Стилизация в этом случае требует проверки попадания даты в любой из диапазонов:
function isInAnyRange(date) {
return ranges.some(range => date >= range.start && date <= range.end);
}
CSS остаётся прежним, но логика добавления классов усложняется за счёт проверки всех интервалов.
Визуальная стилизация диапазона должна строго соответствовать логике выбора. Любое рассогласование между состоянием и отображением приводит к ошибкам восприятия.
Поэтому критически важно:
onSelect и onDrawТак формируется предсказуемое поведение календаря даже при сложной кастомизации внешнего вида.