Кратко
MutationObserver следит за изменениями DOM (добавление/удаление узлов, изменение атрибутов). Selection API читает выделенный пользователем текст. Event Loop определяет порядок выполнения: синхронный код → microtasks (Promise callbacks) → tasks (setTimeout callbacks).
Подробно
Этот урок — три самостоятельные темы, объединённые одной идеей «браузер уведомляет ваш код». Их можно читать по отдельности: наблюдение за DOM (MutationObserver), выделение текста (Selection API) и порядок выполнения асинхронного кода (Event Loop).
1. MutationObserver — наблюдение за изменениями DOM
Обычный код работает «по запросу»: вы вызвали функцию — она выполнилась. MutationObserver работает наоборот: вы говорите браузеру «следи за этим узлом», и он сам вызывает ваш колбэк каждый раз, когда DOM меняется.
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
console.log('Тип:', mutation.type);
console.log('Добавлены:', mutation.addedNodes);
console.log('Удалены:', mutation.removedNodes);
});
});
observer.observe(document.body, {
childList: true, // добавление/удаление дочерних узлов
attributes: true, // изменение атрибутов
subtree: true, // следить за всем поддеревом
characterData: true // изменение текста
});
observer.disconnect(); // остановить наблюдениеВажно: колбэк MutationObserver — это microtask, а не task. Он выполняется после текущего синхронного кода, но до setTimeout.
2. Selection API — выделение текста
Selection API читает текст, который выделил пользователь (мышью или с клавиатуры), и позволяет управлять выделением программно. Range описывает часть документа, а Selection — текущее выделение (набор из одного или нескольких Range).
const selection = document.getSelection();
const selectedText = selection.toString();
console.log('Выделено:', selectedText);
// Выделить текст программно
const range = document.createRange();
range.selectNodeContents(document.querySelector('.text'));
selection.removeAllRanges();
selection.addRange(range);3. Event Loop — порядок выполнения кода
Вы уже знаете, что колбэки Promise попадают в очередь микрозадач (уроки JA4 и JA5). Event Loop — это «диспетчер», который решает, что выполнять следующим. Зачем он нужен: JavaScript однопоточный, поэтому порядок «что выполняется сейчас, а что потом» обязан быть предсказуемым.
Порядок выполнения:
- Синхронный код (call stack) — выполняется сразу, до конца.
- Microtasks (Promise.then, queueMicrotask, MutationObserver) — после синхронного кода, все подряд.
- Tasks (setTimeout, setInterval, I/O) — по одному, между отрисовками.
console.log('1. Синхронный код');
setTimeout(() => {
console.log('4. Task (setTimeout)');
}, 0);
Promise.resolve().then(() => {
console.log('3. Microtask (Promise)');
});
queueMicrotask(() => {
console.log('3. Microtask (queueMicrotask)');
});
console.log('2. Синхронный код');
// Вывод: 1, 2, 3, 3, 4Микрозадачи (microtasks): Promise.then/catch/finally, queueMicrotask(), колбэк MutationObserver. Задачи (tasks): setTimeout, setInterval, обработка I/O.
Ментальная модель
Event Loop — как диспетчер в такси. Синхронный код — текущий пассажир (ездим пока не высадится). Microtasks — VIP-пассажиры (высаживаем всех перед следующим рейсом). Tasks — обычные пассажиры (высаживаем по одному в каждом рейсе). MutationObserver — VIP-звонок: срабатывает после текущей поездки, но до следующей.
Примеры
**Самый простой:**
// MutationObserver
const target = document.querySelector('#list');
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
console.log('Изменение:', mutation.type);
}
});
observer.observe(target, { childList: true });
// Добавляем элемент — observer сработает
target.innerHTML += '<li>Новый</li>';Простейший MutationObserver: следим за добавлением элементов.
**Простой:**
// Порядок выполнения
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
queueMicrotask(() => console.log('4'));
console.log('5');
// Вывод: 1, 5, 3, 4, 2
// Синхронный → Microtasks → TasksКлассический пример порядка: sync → microtask → task.
**Реальный пример:**
// Динамический список с отслеживением
const list = document.querySelector('#todo-list');
let itemCount = 0;
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
itemCount += mutation.addedNodes.length - mutation.removedNodes.length;
document.querySelector('#count').textContent = itemCount;
}
}
});
observer.observe(list, { childList: true });
// Добавляем элементы — счётчик обновляется автоматически
function addItem(text) {
const li = document.createElement('li');
li.textContent = text;
list.appendChild(li);
}Реальный паттерн: автоматический подсчёт элементов через MutationObserver.
Частые ошибки
**Неправильно:** Забывать observer.disconnect()
**Почему:** Без disconnect observer работает бесконечно — утечка памяти.
**Правильно:** Всегда отключайте observer, когда он больше не нужен.
**Неправильно:** Думать, что MutationObserver callback — task
**Почему:** MutationObserver callback — microtask. Он выполняется ДО setTimeout.
**Правильно:** Microtasks: Promise, queueMicrotask, MutationObserver. Tasks: setTimeout, setInterval.
**Неправильно:** Путать Selection и Range
**Почему:** Selection — текущее выделение пользователя. Range — диапазон в DOM.
**Правильно:** Selection содержит Range(ы). Range описывает часть дерева.
Важно запомнить
- MutationObserver следит за изменениями DOM
- observer.observe(target, options) — начать
- observer.disconnect() — остановить
- MutationObserver callback = microtask
- Event Loop: sync → microtasks → tasks
- Selection API читает выделенный текст
Связь
Назад: Вы знаете DOM (D1-D8), события (DA1-DA10) и асинхронный JavaScript (JA1-JA7). Теперь вы видите полную картину: изменения DOM, события и порядок выполнения.
Вперёд: Вы завершили DOM Advanced! Основной путь курса пройден. Дальше по желанию — JS Advanced: дополнительные возможности языка.