Динамическая конфигурация в Popper.js позволяет изменять поведение позиционирования в зависимости от состояния интерфейса, пользовательских действий или внешних условий. В отличие от статической настройки, где параметры задаются один раз при инициализации, динамический подход предполагает обновление опций, модификаторов и даже самих элементов на лету.
Основой является возможность пересоздания или обновления экземпляра Popper с новыми параметрами без полного разрушения логики интерфейса.
setOptionsЭкземпляр Popper предоставляет метод setOptions, который
используется для изменения конфигурации без пересоздания объекта.
const popperInstance = createPopper(reference, popper, {
placement: 'bottom',
});
popperInstance.setOptions({
placement: 'top',
});
setOptions может принимать функцию, получающую текущие
опции:
popperInstance.setOptions((options) => ({
...options,
placement: 'right',
}));
Это позволяет учитывать предыдущую конфигурацию и избегать перезаписи важных параметров.
Модификаторы определяют поведение Popper: смещение, предотвращение выхода за границы, стрелки и др. Их можно изменять во время работы.
popperInstance.setOptions((options) => ({
...options,
modifiers: [
...options.modifiers,
{
name: 'offset',
options: {
offset: [0, 20],
},
},
],
}));
popperInstance.setOptions((options) => ({
...options,
modifiers: options.modifiers.filter(
(modifier) => modifier.name !== 'offset'
),
}));
popperInstance.setOptions((options) => ({
...options,
modifiers: options.modifiers.map((modifier) => {
if (modifier.name === 'offset') {
return {
...modifier,
options: {
offset: [0, 40],
},
};
}
return modifier;
}),
}));
Динамическая конфигурация часто используется в обработчиках событий.
button.addEventListener('click', () => {
popperInstance.setOptions({
placement: 'bottom-start',
});
});
element.addEventListener('mouseenter', () => {
popperInstance.setOptions({
placement: 'top',
});
});
element.addEventListener('mouseleave', () => {
popperInstance.setOptions({
placement: 'bottom',
});
});
Popper можно интегрировать с логикой адаптивности.
matchMediaconst mediaQuery = window.matchMedia('(max-width: 768px)');
function updatePlacement(e) {
popperInstance.setOptions({
placement: e.matches ? 'bottom' : 'right',
});
}
mediaQuery.addEventListener('change', updatePlacement);
updatePlacement(mediaQuery);
Иногда требуется изменить якорный элемент или сам popper.
popperInstance.destroy();
const newInstance = createPopper(newReference, newPopper, {
placement: 'top',
});
Popper поддерживает виртуальные элементы:
const virtualElement = {
getBoundingClientRect: () => ({
width: 0,
height: 0,
top: mouseY,
left: mouseX,
right: mouseX,
bottom: mouseY,
}),
};
createPopper(virtualElement, popper);
Это позволяет динамически привязывать позицию к координатам курсора.
popperInstance.update();
Используется при изменениях DOM, которые Popper не может отследить автоматически.
await popperInstance.update();
Полезно при работе с асинхронными изменениями интерфейса.
Модификаторы могут содержать динамическую логику.
const dynamicOffset = {
name: 'offset',
options: {
offset: ({ placement }) => {
if (placement === 'top') {
return [0, 10];
}
return [0, 20];
},
},
};
Такой подход позволяет изменять поведение без пересоздания конфигурации.
state)Каждый модификатор получает доступ к состоянию Popper:
const customModifier = {
name: 'custom',
enabled: true,
phase: 'main',
fn({ state }) {
console.log(state.placement);
},
};
Это позволяет:
useEffect(() => {
popperInstance.setOptions({
placement: isOpen ? 'bottom' : 'top',
});
}, [isOpen]);
watch(isOpen, (value) => {
popperInstance.setOptions({
placement: value ? 'right' : 'left',
});
});
Частое изменение конфигурации может приводить к лишним перерасчётам.
let timeout;
function updatePopper() {
clearTimeout(timeout);
timeout = setTimeout(() => {
popperInstance.update();
}, 100);
}
function setMode(mode) {
const configs = {
tooltip: { placement: 'top' },
dropdown: { placement: 'bottom-start' },
context: { placement: 'right-start' },
};
popperInstance.setOptions(configs[mode]);
}
document.addEventListener('contextmenu', (e) => {
e.preventDefault();
const virtualElement = {
getBoundingClientRect: () => ({
width: 0,
height: 0,
top: e.clientY,
left: e.clientX,
right: e.clientX,
bottom: e.clientY,
}),
};
popperInstance.destroy();
popperInstance = createPopper(virtualElement, popper, {
placement: 'right',
});
});
Динамическая конфигурация позволяет строить гибкие системы:
Главное преимущество — возможность реагировать на изменения среды без полной перерисовки интерфейса и без потери производительности.