ResizeObserver: изменение размера
ResizeObserver— браузерный API для асинхронного наблюдения за изменениями размеров конкретного элемента, более точный и производительный чем слушатель событияresizeнаwindow.
Зачем нужно
Событие window.resize срабатывает только при изменении размера окна, но элемент может менять размер из-за CSS-изменений, динамического контента, flex/grid-пересчёта — независимо от окна. ResizeObserver наблюдает за конкретным элементом и вызывает callback асинхронно (после layout, перед paint), не блокируя рендеринг.
Где используется
- Адаптивные canvas и SVG (пересчёт при изменении контейнера)
- Виртуальные списки (recalc при изменении высоты элементов)
- Компоненты-диаграммы и графики (Chart.js, D3)
- Responsive component design (не window, а конкретный блок)
- Infinite scroll с динамической высотой элементов
Основной контент
Базовое использование
const observer = new ResizeObserver((entries) => {
entries.forEach(entry => {
const { width, height } = entry.contentRect;
console.log(`Новый размер: ${width}px x ${height}px`);
// Альтернатива через borderBoxSize (с паддингами)
if (entry.borderBoxSize) {
const [box] = entry.borderBoxSize;
console.log(`Border box: ${box.inlineSize}px x ${box.blockSize}px`);
}
});
});
const element = document.querySelector('.resizable-container');
observer.observe(element);
// Остановить наблюдение
observer.unobserve(element);
observer.disconnect(); // остановить всё
Адаптивный canvas
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
// Обновляем размер canvas с учётом devicePixelRatio
const dpr = window.devicePixelRatio || 1;
canvas.width = width * dpr;
canvas.height = height * dpr;
ctx.scale(dpr, dpr);
// Перерисовываем содержимое
redraw(ctx, width, height);
}
});
resizeObserver.observe(canvas.parentElement);
function redraw(ctx, width, height) {
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = '#4a90e2';
ctx.fillRect(10, 10, width - 20, height - 20);
}
Responsive компонент
class ResponsiveChart {
constructor(container) {
this.container = container;
this.observer = new ResizeObserver(entries => {
const entry = entries[0];
const { width } = entry.contentRect;
this.updateLayout(width);
});
this.observer.observe(container);
}
updateLayout(width) {
if (width < 400) {
this.container.classList.add('compact');
} else {
this.container.classList.remove('compact');
}
this.render(width);
}
render(width) {
// перерисовать с новой шириной
}
destroy {
this.observer.disconnect();
}
}
Наблюдение за несколькими элементами
const observer = new ResizeObserver(entries => {
entries.forEach(({ target, contentRect }) => {
// target — элемент, который изменился
console.log(target.id, contentRect.width);
});
});
document.querySelectorAll('.panel').forEach(panel => {
observer.observe(panel);
});
Частые ошибки
- Бесконечный цикл — если callback изменяет размер самого наблюдаемого элемента, это вызывает новое срабатывание. Браузер выбрасывает
ResizeObserver loop limit exceeded. Избегайте синхронных изменений размера в callback. - Забыть
disconnect— наблюдатель держит ссылку на элемент. В React-компонентах вызывайтеdisconnectвuseEffectcleanup. - Путаница contentRect и borderBoxSize —
contentRectбез padding/border,borderBoxSizeвключает их. Выбирайте в зависимости от задачи.