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

В Pikaday позиционирование календаря строится вокруг привязки к DOM-элементу поля ввода и его контейнеру. Библиотека по умолчанию использует position: absolute, вычисляя координаты относительно ближайшего позиционированного родителя или body, если явный контейнер не задан.

Ключевая идея архитектуры заключается в том, что календарь не «живет» внутри инпута, а рендерится отдельным DOM-узлом, который динамически перемещается и позиционируется поверх интерфейса.

Базовое поведение позиционирования

При создании экземпляра календаря:

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

Основной режим:

  • календарь позиционируется относительно document.body
  • используется абсолютное позиционирование
  • координаты пересчитываются при открытии

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


Параметр container

Одним из ключевых инструментов управления позиционированием является опция container.

new Pikaday({
    field: document.getElementById('date'),
    container: document.getElementById('calendar-wrapper')
});

Поведение при использовании container

  • календарь вставляется внутрь указанного DOM-элемента
  • позиционирование становится относительно этого контейнера
  • исчезает зависимость от body
  • появляется контроль над overflow, transform, z-index

Практическое значение

Использование container позволяет:

  • ограничить календарь модальным блоком
  • встроить datepicker в сложные layout-сетки
  • избежать конфликтов с position: fixed родительских элементов
  • управлять clipping через overflow: hidden/auto

Абсолютное позиционирование внутри контейнера

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

  • координаты вычисляются относительно контейнера
  • учитывается offsetParent
  • применяется getBoundingClientRect() для расчётов

Важно учитывать, что:

  • контейнер должен иметь position: relative, absolute или fixed
  • иначе координаты могут рассчитываться некорректно
.calendar-wrapper {
    position: relative;
}

Поведение без контейнера

Если container не указан:

  • календарь добавляется в document.body
  • позиция рассчитывается относительно viewport
  • используется глобальная система координат

Это упрощает использование, но создаёт типичные проблемы:

  • конфликт с overflow: hidden родительских блоков
  • перекрытие элементов с высоким z-index
  • некорректное поведение внутри модальных окон

Опция bound

Внутренний механизм ограничения позиционирования управляется параметром bound.

new Pikaday({
    field: document.getElementById('date'),
    bound: true
});

Логика работы bound

При включённом bound:

  • календарь старается оставаться в пределах viewport
  • автоматически меняет направление открытия
  • предотвращает выход за нижнюю границу окна

При отключённом:

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

Автопереворот (flip) позиции

Хотя явного API «flip» нет, поведение реализовано внутри логики расчёта позиции:

  • если снизу недостаточно места → календарь открывается вверх
  • если сверху недостаточно места → вниз
  • учитывается высота календаря и viewport

Алгоритм основан на сравнении:

  • spaceBelow = window.innerHeight - inputBottom
  • spaceAbove = inputTop

Пересчёт позиции при скролле и ресайзе

Позиционирование не является статичным. Внутри Pikaday реализованы обработчики:

  • scroll
  • resize
  • orientationchange

При изменении состояния окна:

  • координаты пересчитываются
  • календарь «приклеивается» к input
  • предотвращается рассинхронизация при прокрутке

reposition и динамическое обновление координат

Внутренний метод reposition() отвечает за перерасчёт положения календаря.

Он вызывается:

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

Логика:

  1. получить bounding box input
  2. вычислить размеры календаря
  3. определить оптимальное направление открытия
  4. применить top/left

Влияние CSS transform на позиционирование

Один из самых частых источников ошибок — использование transform у родительских элементов.

При наличии:

.parent {
    transform: translate3d(0, 0, 0);
}

возникает эффект:

  • position: absolute начинает считаться относительно трансформированного контейнера
  • координаты могут смещаться

Решения:

  • использовать container на уровне layout-блока
  • избегать transform у предков
  • или изолировать календарь в body

Z-index и слои отображения

Календарь использует собственный слой отображения:

  • z-index задаётся по умолчанию
  • может конфликтовать с модальными окнами

Типичные проблемы:

  • календарь скрывается под overlay
  • перекрывается dropdown-меню

Решения:

  • повышение z-index календаря через CSS
  • размещение внутри контейнера с нужным stacking context

Inline-режим и отсутствие позиционирования

В inline-режиме календарь вообще не использует абсолютное позиционирование:

  • он становится частью DOM-структуры
  • не зависит от координат input
  • не выполняет расчёт позиции

Это полностью меняет модель поведения:

  • нет всплытия
  • нет пересчёта координат
  • нет конфликтов с viewport

Контейнеры внутри модальных окон

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

  • модалка часто имеет overflow: hidden
  • позиционирование может обрезаться

Рекомендуемая структура:

  • контейнер календаря размещается внутри модального body
  • position: relative у модального контента
  • отсутствие clip-обрезки

Поведение при динамическом изменении DOM

При изменении структуры страницы:

  • перемещение input в другой контейнер
  • изменение размеров родителя
  • виртуальный рендеринг (SPA)

Pikaday не всегда автоматически адаптируется, если экземпляр не пересоздаётся.

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

  • recalculation может не учитывать новые стили
  • требуется повторный reposition()
  • иногда необходим destroy + recreate

Ограничения позиционирования в сложных layout

Сложные кейсы включают:

  • таблицы с overflow
  • flex/grid контейнеры с трансформациями
  • скроллируемые панели

Проблемы:

  • потеря привязки к input при scroll container
  • неправильные координаты из-за nested scrolling
  • clipping внутри overflow:auto

Обходные стратегии:

  • вынесение контейнера календаря в root-уровень
  • использование container вне scroll-областей
  • минимизация трансформаций на родителях

Координатная модель расчёта

Внутренне позиционирование базируется на:

  • getBoundingClientRect() input
  • текущем scroll offset
  • высоте календаря
  • размерах viewport

Итоговая формула положения:

  • top = inputBottom + scrollY
  • left = inputLeft + scrollX

с корректировкой на overflow и bound-логику.


Поведение при фиксированных элементах

При position: fixed у контейнеров:

  • координаты могут рассчитываться в viewport-системе
  • скролл страницы не влияет на позицию
  • возможны расхождения при nested scroll

Рекомендуется:

  • избегать вложенных fixed-контейнеров
  • использовать body-level контейнер
  • или полностью inline режим

Управление стабильностью позиции

Для устойчивого поведения в сложных интерфейсах важны:

  • фиксированный контейнер календаря
  • отсутствие трансформаций у родителей
  • корректный z-index контекст
  • регулярный вызов reposition() при изменениях layout

В результате календарь сохраняет визуальную привязку к input даже при динамически изменяющейся странице.