MutationObserver, IntersectionObserver
Glossary overview

MutationObserver, IntersectionObserver

MutationObserver — это встроенный в браузер API, который позволяет асинхронно следить за изменениями в DOM-дереве: добавление/удаление элементов, изменение атрибутов, текста и т.д.

Его преимущество — это более производительный и современный способ, чем старые события вроде DOMSubtreeModified, которые были медленными.

Настройки (options):

ОпцияЧто отслеживает
childListДобавление/удаление дочерних элементов
attributesИзменения атрибутов (например class, id)
characterDataИзменения текста внутри элемента
subtreeНаблюдать за всеми потомками тоже
attributeFilterСписок атрибутов, которые нужно отслеживать
attributeOldValueСохранять старое значение атрибута
characterDataOldValueСохранять старый текст

В этой строке перебираются все MutationRecord-объекты, которые описывают все изменения, произошедшие с наблюдаемым элементом (el), за один «тик».

mutations.forEach(mutation => {...

Что такое mutations? Когда срабатывает MutationObserver, он получает массив объектов MutationRecord, каждый из которых описывает одно изменение:

[
  { type: "attributes", target: ..., attributeName: "class", ... },
  { type: "attributes", target: ..., attributeName: "style", ... }
]

Пример — что может быть внутри mutation

Каждый mutation — это объект с такими свойствами:

СвойствоЧто означает
type'attributes', 'childList', 'characterData'
targetDOM-элемент, в котором произошло изменение
attributeNameИмя изменённого атрибута (если type === 'attributes')
addedNodesУзлы, добавленные в DOM (если childList)
removedNodesУзлы, удалённые из DOM
oldValueСтарое значение (если опция attributeOldValue включена)

Пример простого использования

HTML

<div id="container">
  <p>Начальный текст</p>
</div>

JavaScript

const target = document.getElementById('container');

const observer = new MutationObserver((mutationsList) => {
  for (const mutation of mutationsList) {
    console.log('Что-то изменилось:', mutation);
  }
});

observer.observe(target, {
  childList: true, // следить за добавлением/удалением детей
  subtree: true    // следить не только за target, но и за потомками
});

Это полезно, когда ты хочешь отследить динамические изменения на странице (например, когда другой скрипт меняет DOM).

Примеры

1. Отслеживание добавления новых элементов

const container = document.getElementById('container');

const observer = new MutationObserver((mutations) => {
  mutations.forEach(mutation => {
    if (mutation.type === 'childList') {
      console.log('Добавлены или удалены дочерние элементы');
    }
  });
});

observer.observe(container, { childList: true });

2. Отслеживание изменения атрибутов

const el = document.getElementById('myDiv');

const observer = new MutationObserver(mutations => {
  mutations.forEach(mutation => {
    if (mutation.type === 'attributes') {
      console.log(`Атрибут "${mutation.attributeName}" изменился`);
    }
  });
});

observer.observe(el, { attributes: true });

3. Отслеживание текста (например, изменение цены, названия и т.д.)

const price = document.querySelector('.price');

const observer = new MutationObserver(mutations => {
  mutations.forEach(mutation => {
    console.log('Текст изменился:', mutation.target.textContent);
  });
});

observer.observe(price, { characterData: true, subtree: true });

Как остановить наблюдение

observer.disconnect();

Важно

  • Не вызывается мгновенно: срабатывает асинхронно — после изменений, но до следующего рендера.
  • Используй с умом — если наблюдаешь за большим деревом (subtree: true), это может повлиять на производительность.

Когда применять:

  • Автоматическая реакция на динамические изменения в DOM (например, если другой скрипт меняет содержимое)
  • Отслеживание вставки AJAX-контента
  • Живые виджеты, которые должны обновляться при изменении других элементов
  • Тестирование или отладка DOM-изменений

IntersectionObserver — отслеживание видимости элемента

Это встроенный API в браузерах, который позволяет отслеживать, когда элемент появляется в области видимости (в viewport или другом контейнере). Очень удобно для ленивой загрузки изображений или анимаций при прокрутке.

Синтаксис:

const observer = new IntersectionObserver(callback, options);
observer.observe(targetElement);
  • callback — функция, которая будет вызвана при пересечении элемента и области видимости
  • options — настройки: отступы, пороги и контейнер
  • targetElement — DOM-элемент, за которым мы наблюдаем

Настройки options:

{
  root: null,             // по умолчанию — viewport
  rootMargin: '0px',      // отступы (можно как '100px 0px 100px 0px')
  threshold: 0.5          // процент видимости (от 0 до 1)
}
  • root: контейнер, относительно которого проверять видимость (по умолчанию null = окно браузера)
  • rootMargin: как padding вокруг root (часто 0px 0px 200px 0px, чтобы срабатывало чуть раньше)
  • threshold: насколько элемент должен быть видим, чтобы сработать

Как понять threshold:

ThresholdЧто это значит
0Сразу, как только хоть 1px элемента виден
1Когда весь элемент полностью в области
0.5Когда половина элемента видна

Как остановить наблюдение:

observer.unobserve(element);

Пример: Ленивая загрузка изображений

HTML:

<img data-src="image.jpg" alt="photo" class="lazy-img" />

JavaScript:

const images = document.querySelectorAll('.lazy-img');

const callback = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;   // подгружаем настоящее изображение
      observer.unobserve(img);     // отключаем отслеживание
    }
  });
};

const options = {
  threshold: 0.1 // сработает, когда хотя бы 10% элемента видно
};

const observer = new IntersectionObserver(callback, options);

images.forEach(img => observer.observe(img));

Пример: когда блоки попадают в зону видимости при прокрутке, к ним добавляется класс с анимацией появления.

HTML:

 <div class="box">Блок 1</div>
<div class="box">Блок 2</div>
<div class="box">Блок 3</div>
<div class="box">Блок 4</div>

CSS (для анимации):

.box {
  opacity: 0;
  transform: translateY(50px);
  transition: all 0.6s ease-out;
  height: 200px;
  margin: 50px 0;
  background: lightblue;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
}

.box.visible {
  opacity: 1;
  transform: translateY(0);
}

JavaScript (IntersectionObserver):

const boxes = document.querySelectorAll('.box');

const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      observer.unobserve(entry.target); // отключаем наблюдение после появления
    }
  });
}, {
  threshold: 0.1
});

boxes.forEach(box => observer.observe(box));

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