Popper.js предоставляет мощный механизм управления позиционированием элементов через lifecycle hooks — функции обратного вызова, которые позволяют вмешиваться в процесс расчёта и обновления положения всплывающих элементов (popper). Эти хуки обеспечивают полный контроль над поведением поппера на всех этапах его жизненного цикла.
Жизненный цикл Popper.js можно разделить на несколько ключевых этапов:
Создание экземпляра При вызове
createPopper(reference, popper, options) происходит
инициализация объекта Popper. На этом этапе хуки могут использоваться
для настройки начальных состояний, установки стилей и подготовки данных
о позиционировании.
Перед расчётом позиции (beforeWrite / beforeUpdate) Хуки этого типа вызываются до вычисления координат поппера. Здесь можно изменить размеры, настроить ограничения или модифицировать reference/ popper элементы.
const popperInstance = Popper.createPopper(referenceElement, popperElement, {
modifiers: [
{
name: 'beforeUpdateHook',
phase: 'beforeWrite',
fn({ state }) {
console.log('Состояние перед вычислением:', state);
}
}
]
});
Ключевые свойства объекта state на этом этапе:
state.rects.reference — размеры и положение reference
элемента.state.rects.popper — размеры и положение поппера.state.modifiersData — данные, полученные от других
модификаторов.state.options — текущие опции Popper.js.Расчёт позиции (main / compute) На этом этапе
Popper.js вычисляет точные координаты и оптимальное расположение
всплывающего элемента. Модификаторы с фазой main
выполняются после расчёта, но до применения стилей.
Пример модификатора для изменения смещения:
const offsetModifier = {
name: 'customOffset',
phase: 'main',
fn({ state }) {
state.modifiersData.popperOffsets.x += 10;
state.modifiersData.popperOffsets.y += 5;
}
};Применение изменений (write) Фаза
write выполняется после расчёта позиции и отвечает за
непосредственное применение стилей и атрибутов к
DOM.
const writeHook = {
name: 'logPosition',
phase: 'write',
fn({ state }) {
console.log('Поппер установлен на:', state.styles.popper);
}
};
На этом этапе можно вмешиваться в:
state.styles.popper — стили, которые будут применены к
popper элементу.state.attributes.popper — атрибуты DOM, например
aria или data-*.Обновление и уничтожение После применения стилей Popper.js продолжает отслеживать изменения, которые могут повлиять на позицию (resize, scroll, mutation). Хуки могут быть вызваны при каждом обновлении:
afterWrite — вызывается после применения стилей,
полезен для логирования или запуска анимаций.destroy — вызывается при вызове
popperInstance.destroy(), позволяет очищать ресурсы и
слушатели событий.Фазы хуков Popper.js использует следующие
основные фазы:
beforeRead → read → afterRead → beforeMain → main → afterMain → beforeWrite → write → afterWrite.
Каждая фаза имеет своё назначение и возможность влиять на объект
state.
Модификаторы как хуки Любой пользовательский
модификатор может выступать в роли lifecycle hook, если указать фазу и
функцию fn. Это позволяет:
Контекст state Все хуки получают
один и тот же объект state, содержащий:
elements — ссылки на popper и reference.modifiersData — промежуточные результаты
модификаторов.styles и attributes — применяемые к
DOM.Асинхронные операции Для асинхронных действий
стоит использовать отдельный механизм, так как хуки выполняются
синхронно в жизненном цикле Popper.js. Например, для динамической
загрузки данных лучше обновлять поппер через
popperInstance.update() после завершения асинхронной
операции.
Добавление динамического смещения
const dynamicOffset = {
name: 'dynamicOffset',
phase: 'main',
fn({ state }) {
const scrollFactor = window.scrollY / 10;
state.modifiersData.popperOffsets.y += scrollFactor;
}
};Логирование координат перед применением
const logBeforeWrite = {
name: 'logBeforeWrite',
phase: 'beforeWrite',
fn({ state }) {
console.log('Координаты до применения:', state.modifiersData.popperOffsets);
}
};Очистка слушателей при уничтожении
const cleanupHook = {
name: 'cleanup',
phase: 'destroy',
fn({ state }) {
console.log('Popper уничтожен, очищаем ресурсы');
}
};beforeWrite и write оптимальны для
модификации стилей и атрибутов.state.update().Lifecycle hooks Popper.js представляют собой универсальный инструмент для управления позиционированием, предоставляя детальный доступ к состоянию и возможность вмешательства на всех этапах жизненного цикла.