Руководство по использованию ResizeObserver в JavaScript

Руководство по использованию ResizeObserver в JavaScript
Содержание:
  1. Что такое Resize Observer?
  2. Как использовать Resize Observer?
  3. Методы unobserve() и disconnect()
  4. Примеры использования Resize Observer
  5. Комментарии

В этой статье изучим API Resize Observer, с помощью которого мы можем следить за изменениями размеров элементов. По сути он представляет собой своего рода аналог события resize для элементов.

Что такое Resize Observer?

Resize Observer — это новый web API, который очень похож на другие интерфейсы наблюдателей (Intersection Observer и Mutation Observer). С помощью него мы можем следить за изменениями размеров конкретных элементов.

При этом необходимо отметить, что Resize Observer не выполняет слежение за элементами во время их CSS трансформаций, а также не работает с незаменяемыми инлайновыми элементами. Под незаменяемыми инлайновыми элементами понимаются такие, которые отображаются так как есть. То есть вместо которых не появляется какой-то внешнее заменяющее их содержимое (например, изображение, аудио, видео и так далее).

Кроме этих особенностей, Resize Observer имеет ещё другие. Он информирует нас, когда:

  • наблюдаемый элемент вставляется или удаляется из DOM;
  • свойство display наблюдаемого элемента принимает значение none.

В настоящее время Resize Observer – это единственный способ, с помощью которого мы можем реагировать на изменение размера того или иного элемента. По сути – это своего рода аналог события resize объекта window, но только для элементов.

Пример использования события resize:

JavaScript
window.onresize = () => {
  // ...
}
// или с помощью addEventListener
window.addEventListener('resize', () => {
  // ...
});

При этом событие resize будет происходить при каждом изменении размеров области просмотра, а не только когда фактически изменяется размер интересующего нас элемента.

В результате, использование resize может привести к большим проблемам с производительностью, чем применение Resize Observer. Так как количество возникающий событий будет больше и для их обработки потребуется намного больше времени и ресурсов.

На текущий момент, Resize Observer имеет поддержку во всех основных современных браузерах:

  • Chrome >= 64;
  • Edge >= 79;
  • Firefox >= 69;
  • Safari >= 13.1.

Посмотреть это более детально можно на ресурсе «Can I use...».

Объект ResizeObserverEntry

Как использовать Resize Observer?

Использование Resize Observer осуществляется путем создания нового экзмепляра класса ResizeObserver и использовании в качестве аргумента функции обратного вызова callback:

JavaScript
const resizeObserver = new ResizeObserver(callback);

В качестве callback обычно используют анонимную стрелочную функцию:

JavaScript
const resizeObserver = new ResizeObserver((entries, observer) => {
  console.log(entries);
  console.log(observer);
});
Объект ResizeObserverEntry

Эта функция будет вызываться всякий раз, когда будет происходить изменение размеров у наблюдаемых элементов.

Она имеет 2 параметра:

  • entries – содержит массив объектов ResizeObserverEntry, который мы можем использовать для получения новых размеров элемента.
  • observer – хранит ссылку на сам ResizeObserver, с помощью которой мы можем вызовать методы этого объекта внутри колбэк функции.

Параметр entries, как мы уже отметили выше, содержит массив объектов. Чтобы их перебрать мы можем, например, использовать метод forEach:

JavaScript
const resizeObserver = new ResizeObserver((entries, observer) => {
  entries.forEach((entry) => {
    console.log(entry);
  });
});
Объект ResizeObserverEntry

Объект ResizeObserverEntry имеет следующие свойства:

  • borderBoxSize – общие размеры элемента (включают padding и border);
  • contentBoxSize – размеры контентной области;
  • contentRect – возвращает объект DOMRectReadOnly, содержащий информацию о положении и размерах элемента;
  • devicePixelContentBoxSize – возвращает размеры элемента в физических пикселях;
  • target – содержит наблюдаемый DOM-элемент, размеры которого изменились.

Свойства borderBoxSize, contentBoxSize и devicePixelContentBoxSize возвращают объект ResizeObserverSize. Он включает в себя два свойства: inlineSize и blockSize. Они будут обозначать ширину и высоту элемента, при условии, что мы работаем в горизонтальном режиме письма.

Последнее, что нужно сделать – это добавить элементы (Element или SVGElement) за изменениями размеров которых resizeObserver должен наблюдать. Осуществляется это с помощью метода observe. Например, добавим 2 элемента:

JavaScript
resizeObserver.observe(document.querySelector('.box-1'));
resizeObserver.observe(document.querySelector('.box-2'));

В результате код, необходимый для наблюдения за изменениями размеров двух элементов, будет следующим:

JavaScript
const resizeObserver = new ResizeObserver((entries, observer) => {
  entries.forEach((entry) => {
    const width = entry.borderBoxSize[0].blockSize.toFixed(2);
    const height = entry.borderBoxSize[0].inlineSize.toFixed(2);
    entry.target.textContent = `${width} x ${height}`;
  });
});

resizeObserver.observe(document.querySelector('.box-1'));
resizeObserver.observe(document.querySelector('.box-2'));
Пример наблюдения за 2 элементами с помощью Resize Observer

Данная задача до появления ResizeObserver, то есть с помощью события resize решалась бы следующим образом:

JavaScript
const elBoxes = document.querySelectorAll('.box');

const resizeBoxes = () => {
  elBoxes.forEach((elBox) => {
    const clientRect = elBox.getBoundingClientRect();
    const width = clientRect.width.toFixed(2);
    const height = clientRect.height.toFixed(2);
    elBox.textContent = `${width} x ${height}`;
  });
}

window.addEventListener('resize', resizeBoxes);

resizeBoxes();

Методы unobserve() и disconnect()

Кроме observe(), с помощью которого мы можем включить слежение за элементом, имеются ещё методы unobserve() и disconnect().

Метод unobserve() позволяет остановить слежение за указанным элементом. Для этого элемент за которым нужно прекратить наблюдение необходимо передать в качестве аргумента:

JavaScript
// el – элемент, за которым нужно прекратить наблюдение 
resizeObserver.unobserve(el);

Метод disconnect() предназначен, когда нужно остановить наблюдение за всеми элементами:

JavaScript
resizeObserver.disconnect();

Примеры использования Resize Observer

1. Изменение размера шрифта в зависимости от ширины элемента:

JavaScript
// DOM-элемент, содержащий контент
const elContent = document.querySelector('.content');
// чекбокс, определяющий нужно ли сделать размер шрифта адаптивным
const checkbox = document.querySelector('#fz-adaptive');
//создаем экземпляр ResizeObserver
const resizeObserver = new ResizeObserver((entries, observer) => {
  entries.forEach((entry) => {
    // вычисленный размер шрифта, который получаем путём деления ширины элемента на 800
    const calcFontSize = entry.contentBoxSize[0].inlineSize / 800;
    // в качестве размера шрифта устанавливаем большое из двух значений: 1 и calcFontSize 
    elContent.style.fontSize = `${Math.max(1, calcFontSize )}rem`;
  });
});

// устанавливаем наблюдение за элементом elContent
resizeObserver.observe(elContent);

// при клике на чекбокс
checkbox.addEventListener('change', (e) => {
  if (checkbox.checked) {
    // устанавливаем наблюдение за элементом elContent
    resizeObserver.observe(elContent);
  } else {
    // удаляем наблюдение за элементом elContent
    resizeObserver.unobserve(elContent);
    // устанавливаем в качестве размера шрифта значение 1rem 
    elContent.style.fontSize = '1rem';
  }
});
Изменение размера шрифта в зависимости от ширины элемента с помощью Resize Observer

2. Создание графики на <canvas>, которая перерисовывается при изменении размера холста:

JavaScript
const elCanvas = document.querySelector('canvas');
const context = elCanvas.getContext('2d');

const resizeObserver = new ResizeObserver((entries) => {
  entries.forEach((entry) => {
    const width = entry.borderBoxSize[0].inlineSize;
    const height = entry.borderBoxSize[0].blockSize;
    [elCanvas.width, elCanvas.height] = [width, height];
    context.clearRect(0, 0, width, height);

    const cx = width / 2;
    const cy = height / 2;
    const k = Math.min(width, height) / 150;

    context.lineWidth = 5 * k;

    context.fillStyle = '#f0cf28';
    context.beginPath();
    context.arc(cx, cy, 50 * k, 0, Math.PI * 2, true);
    context.fill();

    context.beginPath();
    context.arc(cx, cy, 25 * k, 0 + Math.PI / 8, Math.PI - Math.PI / 8, false);
    context.stroke();

    context.fillStyle = '#000';
    context.beginPath();
    context.arc(cx - 20 * k, cy - 10 * k, 5 * k, 0, Math.PI * 2, true); // Left eye
    context.fill();

    context.arc(cx + 20 * k, cy - 10 * k, 5 * k, 0, Math.PI * 2, true); // Right eye
    context.fill();
  });
});

resizeObserver.observe(document.documentElement);
Создание масштабируемой графики на <canvas> с использование ResizeObserver

3. Слайдер, в котором величина смещения слайдов будет перерасчитываться при изменении его размеров:

JavaScript
const elSlider = document.querySelector('.slider');
const elSliderItems = document.querySelector('.slider-items');

const calcWidthItem = (widthItem) => {
  const width = elSlider.getBoundingClientRect().width;
  const count = Math.floor(width / widthItem);
  const gaps = width - widthItem * count;
  return widthItem + gaps / (count - 1);
}

const transform = () => {
  const index = elSlider.dataset.index ?? 0;
  const value = -elSlider.dataset.width * elSlider.dataset.index;
  elSliderItems.style.transform = `translateX(${value}px)`;
}

document.addEventListener('click', (e) => {
  if (e.target && !e.target.closest('.slider-btn')) {
    return;
  }
  const btnSlider = e.target.closest('.slider-btn');
  elSlider.dataset.index = btnSlider.dataset.index;
  transform();
});

const resizeObserver = new ResizeObserver((entries) => {
  entries.forEach((entry) => {
    elSlider.dataset.width = calcWidthItem(entry.borderBoxSize[0].inlineSize);
    transform();
  });
});

resizeObserver.observe(document.querySelector('.slider-item'));
Слайдер, в котором значение смещения в горизонтальном направлении будем перерасчитывать при изменении размера слайдера

Комментарии: 0