Table of Contents
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' |
target | DOM-элемент, в котором произошло изменение |
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));
Когда ты прокручиваешь страницу, каждый блок плавно появляется снизу вверх. Это часто используется для эффектных лендингов и презентационных сайтов.