MutationObserver を複数回セットアップすると、同じ要素に複数のオブザーバーが重複して登録される。グローバルな Set や Map で管理する方法もあるが、dataset に観測済みフラグを書き込む方法は軽量で移植しやすい。
コード
const OBSERVE_ID_KEY = 'observeId'
const isElementObserved = <T extends HTMLElement = HTMLElement>( ele: T) => Object.hasOwn(ele.dataset, OBSERVE_ID_KEY)
const setObserverId = (id: string) => <T extends HTMLElement = HTMLElement>(ele: T): T => { ele.dataset[OBSERVE_ID_KEY] = id return ele }
export const observeElement = ( observeId: string, element: HTMLElement | string, observerCallback: MutationCallback, options: MutationObserverInit = { childList: true }) => { const observedElement = element instanceof HTMLElement ? element : document.querySelector<HTMLElement>(element) if (observedElement && !isElementObserved(observedElement)) { const observer = new MutationObserver(observerCallback) observer.observe(observedElement, options) setObserverId(observeId)(observedElement) return observer }}使用例
// セレクター文字列でも HTMLElement でも渡せるobserveElement( 'my-list-observer', '#item-list', (mutations) => { for (const mutation of mutations) { console.log('変更を検知:', mutation.type) } })
// 同じ要素に再度呼んでも重複登録されないobserveElement('my-list-observer', '#item-list', callback)仕組み
Object.hasOwn(ele.dataset, OBSERVE_ID_KEY)でdata-observe-id属性の存在を確認する- 未登録の場合のみ
MutationObserverを生成してobserve()を呼ぶ - 観測開始後に
ele.dataset[OBSERVE_ID_KEY] = idでフラグを書き込む - 次回以降の呼び出しでは
isElementObservedがtrueを返すため何もしない
dataset の値は HTML の data-* 属性に直接書き込まれるため、DevTools で確認できる。
メリット
- グローバルレジストリ不要:状態を要素自身に持たせるため、Map や Set を別途管理しなくてよい
- 移植性が高い:フレームワークへの依存がなく、あらゆる環境で動く
- デバッグしやすい:属性値を DevTools で直接確認できる
注意点
要素が DOM から削除されると dataset の情報も消える。同じ要素が再挿入された場合は再びオブザーバーが登録される点に注意すること。また、オブザーバーを止めたい場合は observer.disconnect() を別途呼ぶ必要がある。
実務メモ
このスニペットは、TypeScript、JavaScript、DOM、MutationObserver の周辺で同じ操作や判定を毎回書きたくない時に向く。小さな補助として切り出しておくと、呼び出し側では意図だけを追いやすい。
逆に、分岐や前提条件が増えて責務が膨らむなら、1本のスニペットに詰め込まない方がよい。手順と helper を分けるか、役割ごとに切り出す方が保守しやすい。
hsb.horse