Наблюдатели DOM — Intersection, Resize и Mutation
Проблема scroll-слушателей
Раньше «элемент попал в экран» ловили через window.addEventListener('scroll', …) и getBoundingClientRect() на каждый кадр. Это:
- нагружает главный поток;
- плохо сочетается с вложенными прокручиваемыми контейнерами.
IntersectionObserver и ResizeObserver сообщают браузеру: «скажи, когда изменится видимость или размер» — без ручного опроса координат.
Связь: работа с HTML в JavaScript, виджеты, атрибут loading="lazy" у <img> — в HTML.
IntersectionObserver
Следит, пересекается ли элемент с областью просмотра (viewport или заданный предок).
const images = document.querySelectorAll('img[data-src]');
const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
observer.unobserve(img);
}
},
{
root: null, // viewport
rootMargin: '200px 0px', // подгрузить чуть раньше появления
threshold: 0.01, // доля видимой площади (0…1 или массив)
}
);
images.forEach((img) => observer.observe(img));
Поля entry
| Поле | Смысл |
|---|---|
isIntersecting | есть ли пересечение сейчас |
intersectionRatio | доля видимой площади (0–1) |
target | наблюдаемый элемент |
boundingClientRect | геометрия элемента |
rootBounds | границы root |
Бесконечный список
Наблюдайте сенсор внизу списка; при появлении — fetch следующей страницы и unobserve до вставки новых элементов.
const sentinel = document.querySelector('#list-sentinel');
let loading = false;
const listObserver = new IntersectionObserver(async (entries) => {
const visible = entries.some((e) => e.isIntersecting);
if (!visible || loading) return;
loading = true;
try {
const chunk = await loadNextPage();
appendItems(chunk);
} finally {
loading = false;
}
});
listObserver.observe(sentinel);
Пауза видео и анимаций
Когда блок ушёл с экрана — остановить requestAnimationFrame или <video>:
const carousel = document.querySelector('.promo-carousel');
const visibilityObserver = new IntersectionObserver(
(entries) => {
const onScreen = entries[0]?.isIntersecting;
document.dispatchEvent(
new CustomEvent('carousel:visibility', { detail: { onScreen } })
);
},
{ threshold: 0.25 }
);
visibilityObserver.observe(carousel);
root — прокрутка внутри контейнера
const scrollBox = document.querySelector('.modal__body');
const io = new IntersectionObserver(callback, { root: scrollBox });
ResizeObserver
Реагирует на изменение размеров элемента (не окна). Удобно для графиков, canvas, текстовых блоков с переносом.
const chartHost = document.querySelector('#chart');
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentBoxSize?.[0] ?? entry.contentRect;
redrawChart(width, height);
}
});
resizeObserver.observe(chartHost);
// при destroy виджета:
resizeObserver.disconnect();
Отличие от window.resize: срабатывает, когда меняется сам блок (flex, sidebar, @container).
MutationObserver
MutationObserver сообщает, когда в поддереве добавили, удалили или изменили узлы и атрибуты. Колбэк попадает в очередь микрозадач (как Promise.then) — см. event loop.
const host = document.getElementById('chat-messages');
const mo = new MutationObserver((records) => {
for (const record of records) {
if (record.type === 'childList' && record.addedNodes.length) {
host.scrollTop = host.scrollHeight;
}
}
});
mo.observe(host, { childList: true, subtree: true });
Опция observe | Что отслеживать |
|---|---|
childList | добавление/удаление дочерних узлов |
attributes | смена атрибутов (class, data-*, disabled…) |
characterData | текст внутри текстовых узлов |
subtree | изменения во всём потомке, не только прямых детей |
attributeFilter | только перечисленные атрибуты |
Типичные задачи: автопрокрутка чата, синхронизация счётчика элементов, интеграция со сторонним виджетом, который правит DOM без ваших хуков.
Ограничения:
- не заменяет события (
click,input) — для реакции на действия пользователя нужны обработчики; - частые мутации (анимация, посимвольный ввод) могут вызвать лавину колбэков — сужайте
subtreeи фильтры; - при
destroy()виджета вызывайтеdisconnect(), как у других observers.
Сравнение с альтернативами
| Задача | Рекомендация |
|---|---|
| Ленивые картинки | loading="lazy" + при необходимости IO для фона/data-src |
| «В viewport» | IntersectionObserver |
| Размер блока для layout JS | ResizeObserver |
| Изменилась структура DOM | MutationObserver |
| Позиция при каждом scroll | избегать; IO или CSS position: sticky |
Очистка
Наблюдатели держат ссылки на элементы. При удалении узла из DOM:
observer.unobserve(element);
// или полностью:
observer.disconnect();
В методе destroy() виджета отключайте все observers вместе с таймерами и отменой fetch.
Краткий итог
IntersectionObserver — видимость и ленивая подгрузка. ResizeObserver — пересчёт layout при изменении размеров контейнера. MutationObserver — реакция на правки дерева без ручного опроса. Все три снижают нагрузку по сравнению со scroll- и DOM-опросом в цикле и хорошо стыкуются с ванильными компонентами.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Основы JavaScript - стандарт ECMAScript, модель выполнения и базовые конструкции языка. JavaScript — это язык программирования, который позволяет создавать интерактивные веб-страницы, серверные приложения и мобильные программы. Для создания массивов используется литеральная нотация. Конструктор Array не применяется. Как работать с HTML-элементами, как их создавать, менять. Простые приложения на JavaScript - базовые сценарии, структура кода и быстрый старт с практическими примерами. Расширения файлов определяют способ обработки кода средой выполнения или компилятором. История JavaScript - происхождение языка, ключевые этапы развития и влияние на современный веб. Такое именование представляет собой соглашение между разработчиками. Классический JavaScript не обеспечивает реальной приватности через подчеркивания. JavaScript содержит набор зарезервированных слов, которые имеют специальное значение в языке. Эти слова нельзя использовать в качестве идентификаторов для переменных, функций или классов. Встроенные функции JavaScript - ключевые методы массивов, строк и объектов для повседневной разработки. Этот шаблон описывает подключение внешних функций, классов или значений из других файлов. Он используется в начале файла и определяет зависимости текущего модуля. JavaScript используется для создания кроссплатформенных мобильных приложений, которые работают на iOS и Android с использованием единой кодовой базы.Основы JavaScript
Что требуется знать перед началом изучения языка программирования JavaScript
Рекомендации по разработке на JavaScript
Работа с HTML в JavaScript
Простые приложения на JavaScript
Форматы JavaScript
История языка JavaScript
Синтаксис и пунктуация в JavaScript
Ключевые слова языка JavaScript
Встроенные функции JavaScript
Структура и подключение JavaScript-кода
Применение JavaScript в вебе и за его пределами