Popper.js предоставляет мощный механизм кастомизации поведения всплывающих элементов через модификаторы. Модификаторы позволяют вмешиваться в процесс вычисления позиции и управления стилями поппера на каждом этапе жизненного цикла. Каждый модификатор — это объект с набором свойств, которые определяют, когда и как он выполняется.
Ключевые свойства модификатора:
name — уникальное имя модификатора.enabled — логическое значение, активен модификатор или
нет.phase — стадия жизненного цикла, на которой выполняется
модификатор. Доступные фазы: beforeRead, read,
afterRead, beforeMain, main,
afterMain, beforeWrite, write,
afterWrite.fn — основная функция модификатора, получает объект
state и options.effect — функция побочного эффекта, вызывается при
инициализации и уничтожении поппера.requires — массив имён модификаторов, от которых
зависит данный модификатор.requiresIfExists — массив опциональных
зависимостей.stateВнутри функции модификатора fn доступен объект
state, содержащий полную информацию о текущем состоянии
поппера:
state.elements — ссылки на DOM-элементы:
reference и popper.state.styles — текущие CSS-стили для применения к
элементам.state.attributes — атрибуты для элементов, например
aria-expanded.state.modifiersData — данные от всех модификаторов,
которые можно использовать и обновлять.state.rects — размеры и позиции элементов.state.placement — текущее расположение поппера.state.options — глобальные опции Popper.js, включая
массив модификаторов.state.scrollParents — массив родительских элементов с
прокруткой.Простейший модификатор может добавлять смещение поппера по определённой логике:
const offsetModifier = {
name: 'customOffset',
enabled: true,
phase: 'main',
fn({ state, options }) {
const offsetX = options.offsetX || 0;
const offsetY = options.offsetY || 0;
state.styles.popper.top = `${parseFloat(state.styles.popper.top) + offsetY}px`;
state.styles.popper.left = `${parseFloat(state.styles.popper.left) + offsetX}px`;
},
options: {
offsetX: 10,
offsetY: 20
}
};
const popperInstance = Popper.createPopper(referenceElement, popperElement, {
modifiers: [offsetModifier]
});
В этом примере модификатор выполняется на фазе main,
после вычисления стандартной позиции, добавляя дополнительные смещения
по X и Y.
effect для побочных действийМодификатор может создавать побочные эффекты, которые управляют DOM напрямую или подписываются на события:
const borderHighlight = {
name: 'highlightBorder',
enabled: true,
phase: 'write',
fn({ state }) {
state.elements.popper.style.border = '2px solid red';
},
effect({ state }) {
const popper = state.elements.popper;
popper.addEventListener('mouseenter', () => popper.style.borderColor = 'blue');
return () => {
popper.removeEventListener('mouseenter', () => popper.style.borderColor = 'blue');
};
}
};
effect вызывается один раз при инициализации,
возвращаемая функция используется для очистки при уничтожении экземпляра
Popper.js.
modifiersDataМодификаторы могут передавать данные между собой через
state.modifiersData:
const calculateDistance = {
name: 'calculateDistance',
enabled: true,
phase: 'read',
fn({ state }) {
const rect = state.rects.popper;
state.modifiersData.distanceFromTop = rect.top;
}
};
const applyDistanceClass = {
name: 'applyDistanceClass',
enabled: true,
phase: 'write',
requires: ['calculateDistance'],
fn({ state }) {
const distance = state.modifiersData.calculateDistance.distanceFromTop;
if (distance > 100) {
state.elements.popper.classList.add('far-from-top');
} else {
state.elements.popper.classList.remove('far-from-top');
}
}
};
Таким образом, один модификатор вычисляет данные, а другой использует их для управления стилями или классами.
Выбор правильной фазы критически важен для корректного поведения:
Неправильный выбор фазы может привести к конфликту с другими модификаторами или некорректной визуализацией поппера.
Модификаторы можно активировать и деактивировать во время работы:
popperInstance.setOptions({
modifiers: [
{ ...offsetModifier, enabled: false },
{ ...borderHighlight, enabled: true }
]
});
Это позволяет управлять поведением всплывающих элементов в зависимости от состояния интерфейса.
Для надёжной работы модификатора рекомендуется соблюдать следующие принципы:
phase в зависимости от задачи.state.modifiersData для передачи данных
между модификаторами.effect.requires и
requiresIfExists, чтобы избежать конфликтов.Такой подход позволяет создавать мощные, предсказуемые и расширяемые кастомные модификаторы, обеспечивая полный контроль над поведением Popper.js и интеграцию в сложные пользовательские интерфейсы.