Popper.js — это мощная библиотека для управления позиционированием всплывающих элементов относительно целевых элементов в DOM. Одним из ключевых аспектов, влияющих на корректное позиционирование, является изменение размеров окна или родительских элементов. Без корректной обработки этих изменений попперы могут смещаться, накладываться на другие элементы или частично скрываться.
Popper.js использует механизм resize
браузера через объект window для отслеживания глобального
изменения размеров. Основной способ обеспечить корректное
позиционирование при изменении размеров — это интеграция с модулем
autoUpdate или ручное добавление
слушателей событий:
import { createPopper, autoUpdate } from '@popperjs/core';
const reference = document.querySelector('#button');
const popper = document.querySelector('#tooltip');
const popperInstance = createPopper(reference, popper, {
placement: 'top',
});
// Автоматическое обновление при изменении размеров окна или скролла
autoUpdate(reference, popper, popperInstance.update);
Ключевой момент: autoUpdate использует
комбинацию ResizeObserver, событий scroll и
resize для обеспечения динамического позиционирования. Это
означает, что при любом изменении размеров целевого элемента, поппера
или окна вызовется метод update, корректирующий
позицию.
Для более тонкой настройки и наблюдения за отдельными элементами
используется API ResizeObserver, который
позволяет реагировать на изменения размеров конкретного DOM-узла без
постоянного контроля окна:
const resizeObserver = new ResizeObserver(() => {
popperInstance.update();
});
resizeObserver.observe(reference);
resizeObserver.observe(popper);
Особенности:
update при
изменениях, не влияющих на позиционирование.Иногда требуется ограничить количество вызовов метода
update при частых изменениях размеров. Для этого используют
дебаунс или троттлинг:
function debounce(fn, delay) {
let timer;
return () => {
clearTimeout(timer);
timer = setTimeout(fn, delay);
};
}
const debouncedUpdate = debounce(() => popperInstance.update(), 100);
window.addEventListener('resize', debouncedUpdate);
Применение дебаунса позволяет уменьшить нагрузку на браузер при постоянном изменении размеров и обеспечивает плавную работу интерфейсов с Popper.js.
Попперы часто находятся внутри контейнеров, размеры которых изменяются при динамическом контенте. В таких случаях наблюдение за изменениями размеров родителя является обязательным:
const container = document.querySelector('#container');
const containerObserver = new ResizeObserver(() => {
popperInstance.update();
});
containerObserver.observe(container);
Popper.js корректно позиционирует элементы даже при
асинхронной подгрузке контента, если используется
update или autoUpdate в сочетании с
ResizeObserver.
autoUpdate по возможности
— это упрощает работу и снижает количество кода.ResizeObserver, чтобы избежать лишних вычислений.scroll и resize
события для полной синхронизации попперов с интерфейсом.placement и
strategy при значительных изменениях размеров,
чтобы избежать смещения за границы контейнера.placement), при этом применяя
ограничения (preventOverflow, flip) для
корректного отображения.ResizeObserver реагирует не только на изменение ширины
и высоты, но и на трансформации и масштабирование
элементов, что делает его более точным инструментом, чем просто
событие resize окна.import { createPopper, autoUpdate } from '@popperjs/core';
const button = document.querySelector('#button');
const tooltip = document.querySelector('#tooltip');
const container = document.querySelector('#container');
const popperInstance = createPopper(button, tooltip, {
placement: 'bottom',
modifiers: [
{ name: 'flip', options: { fallbackPlacements: ['top', 'right'] } },
{ name: 'preventOverflow', options: { padding: 8 } },
],
});
const observers = [
new ResizeObserver(() => popperInstance.update()),
new ResizeObserver(() => popperInstance.update()),
];
observers[0].observe(button);
observers[1].observe(container);
autoUpdate(button, tooltip, popperInstance.update);
В этом примере Popper.js получает полноценный контроль над изменениями размеров как целевого элемента, так и родительского контейнера, обеспечивая стабильное и корректное позиционирование даже в динамически изменяющемся интерфейсе.