@coraltravelcenter/react-dom-observer
v1.0.1
Published
Observer for React (or any DOM) elements with appear/disappear and mutation hooks
Readme
@coraltravelcenter/react-dom-observer (Full API)
Полнофункциональный обёрточный MutationObserver для отслеживания появления и исчезновения элементов по CSS‑селектору, а также мутаций внутри каждого найденного элемента:
onAppear/onDisappearonChildMutate(изменения детей)onAttributeMutation(изменения атрибутов, с поддержкойattributeFilter)onCharacterData(изменения текстовых узлов)
Без зависимостей. Батчинг через requestAnimationFrame. Автогенерация типов для TypeScript из JSDoc.
Быстрый старт (full)
import ReactDomObserver from "@coraltravelcenter/react-dom-observer";
const obs = new ReactDomObserver(".card", {
debug: true,
observeExisting: true, // обработать уже существующие элементы на старте
watchAttributes: true,
attributeFilter: ["data-state", "aria-expanded"],
watchChild: true,
watchCharacterData: false,
onAppear: (el) => {
console.log("appear:", el);
},
onDisappear: (el) => {
console.log("disappear:", el);
},
onChildMutate: (el, mutations, batch) => {
console.log("childList batch", batch, mutations);
},
onAttributeMutation: (el, mutations, batch) => {
console.log("attributes batch", batch, mutations);
},
onCharacterData: (el, mutations, batch) => {
console.log("characterData batch", batch, mutations);
},
});
obs.start();API
Конструктор
new ReactDomObserver(selector: string, options?: FullObserverOptions)selector: string
CSS‑селектор целевых элементов, за которыми ведётся наблюдение.
options: FullObserverOptions
| Опция | Тип | По умолчанию | Описание |
| --------------------- | -------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------- |
| onAppear | (el: HTMLElement) => void | — | Вызывается при первом обнаружении элемента в DOM (внутри root). |
| onDisappear | (el: HTMLElement) => void | — | Вызывается при исчезновении элемента из DOM/root. |
| onChildMutate | (el, mutations, count) => void | — | Срабатывает на изменения дочерних узлов в каждом наблюдаемом элементе. |
| onAttributeMutation | (el, mutations, count) => void | — | Срабатывает на изменения атрибутов. |
| onCharacterData | (el, mutations, count) => void | — | Срабатывает на изменения текстовых узлов. |
| root | Node | document.body | Контейнер, внутри которого отслеживаем изменения. Полезно для Shadow DOM или узкой области страницы. |
| observeExisting | boolean | true | Вызывать onAppear для уже существующих в DOM элементов при start(). |
| perElementOnce | boolean | true | Вызывать onAppear для каждого конкретного элемента только один раз за жизненный цикл наблюдателя. |
| once | boolean | false | Глобальный режим: после первого onAppear на любом элементе вызовет stop(). |
| debug | boolean | false | Логи в консоль. |
| watchChild | boolean | false | Включить трекинг childList‑мутаций внутри каждого найденного элемента. |
| watchAttributes | boolean | false | Включить трекинг изменений атрибутов. |
| watchCharacterData | boolean | false | Включить трекинг изменений текстовых узлов. |
| attributeFilter | string[] | — | Ограничить наблюдение за атрибутами указанных имён (автоматически включает watchAttributes). |
Внутренний
MutationObserverнастраивается пер‑элементно согласно флагамwatch*. Колбэки мутаций вызываются батчами (пачками) — один вызов на серию мутаций, полученную за тик.
Методы
start(): void— запустить наблюдение (идемпотентно).stop(): void— остановить наблюдение и отписаться от всех внутреннихMutationObserver(идемпотентно).check(): void— принудительно выполнить сканирование DOM прямо сейчас.
Примеры
1) Слежение за раскрытием/сворачиванием элемента
const acc = new ReactDomObserver(".accordion", {
watchAttributes: true,
attributeFilter: ["aria-expanded"],
onAttributeMutation(el, muts) {
const expanded = el.getAttribute("aria-expanded") === "true";
console.log("accordion expanded?", expanded);
},
});
acc.start();2) Инициализация и очистка виджета
const widgets = new ReactDomObserver("[data-widget]", {
onAppear(el) {
el.__widget = bootWidget(el);
},
onDisappear(el) {
el.__widget?.destroy?.();
},
});
widgets.start();3) Подписка на изменения списков внутри элемента
const listObs = new ReactDomObserver(".list", {
watchChild: true,
onChildMutate(el, mutations) {
for (const m of mutations) {
if (m.type === "childList") {
if (m.addedNodes.length) console.log("added:", m.addedNodes);
if (m.removedNodes.length)
console.log("removed:", m.removedNodes);
}
}
},
});
listObs.start();4) Отслеживание конкретных атрибутов и текстовых изменений
const textObs = new ReactDomObserver(".editable", {
watchAttributes: true,
watchCharacterData: true,
attributeFilter: ["data-state"],
onAttributeMutation(el, muts) {
console.log("data-state changed:", el.getAttribute("data-state"));
},
onCharacterData(el, muts) {
console.log("text changed in", el);
},
});
textObs.start();5) Наблюдение в пределах Shadow DOM
const host = document.querySelector("my-widget");
const root = host?.shadowRoot;
const shadowObs = new ReactDomObserver(".item", {
root,
observeExisting: true,
onAppear(el) {
console.log("shadow item:", el);
},
});
shadowObs.start();Поведение и гарантии
- RAF‑батчинг: множество мутаций склеиваются в один
check()на кадр, чтобы не дёргатьquerySelectorAllпо каждой микромутации. - Пер‑элементный внутренний наблюдатель: при
watch*флагах на каждый найденный элемент вешается собственныйMutationObserverсо сквозными опциями (subtree: true). Колбэки мутаций получают всю пачку событий. - Идемпотентность: повторные
start()/stop()безопасны. - Очистка: при
stop()иonDisappearвсе внутренние наблюдатели корректно отписываются. - Семантика
perElementOnce: еслиtrue,onAppearдля одного и того же DOM‑узла будет вызван только единожды (даже при удалении/повторной вставке). Если нужно триггерить заново — ставьperElementOnce: false.
Рекомендации по производительности
- Старайся задавать узкий
selectorи по возможности ограничивать областьrootвместо всегоdocument.body. - Держи колбэки лёгкими: тяжёлую логику (парсинг, загрузки) выноси за пределы кадра.
- Используй
attributeFilterприwatchAttributes, чтобы сократить шум. - Если не требуются повторные срабатывания для одного и того же узла — держи
perElementOnce: true.
Ограничения
- Наблюдение реализовано в браузере; не предназначено для SSR/Node без DOM.
- Плагин не управляет стилями или видимостью во вьюпорте — только DOM‑мутации.
Лицензия
MIT © Михаил / Coral Travel Center
