npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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 / onDisappear
  • onChildMutate (изменения детей)
  • 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