@@ -423,7 +423,7 @@ export class ElementObserverManager {
423423 private resizeObserver : ResizeObserver
424424 private mutationObserver : MutationObserver
425425 private observedElements : Map < HTMLElement , ( ) => void >
426- private pendingReconnectFrame : number | undefined
426+ private isMutationObserverActive = false
427427
428428 private pruneDetachedObservedElements ( ) : void {
429429 for ( const observedElement of Array . from ( this . observedElements . keys ( ) ) ) {
@@ -448,34 +448,59 @@ export class ElementObserverManager {
448448 } )
449449 } )
450450
451- // Configure MutationObserver
451+ // Configure MutationObserver once and only connect/disconnect based on active observed elements.
452452 this . mutationObserver = new MutationObserver ( ( mutations ) => {
453+ if ( this . observedElements . size === 0 ) return
454+
453455 this . pruneDetachedObservedElements ( )
454456 const targets = new Set < HTMLElement > ( )
455457
456458 mutations . forEach ( ( mutation ) => {
457- const target = mutation . target as HTMLElement
458- if ( ! document . contains ( target ) ) return
459- // Find the closest observed element
460- let element = target
459+ let element : HTMLElement | null = null
460+ if ( mutation . target instanceof HTMLElement ) {
461+ element = mutation . target
462+ } else {
463+ element = mutation . target . parentElement
464+ }
465+
466+ if ( ! element || ! document . contains ( element ) ) return
467+
461468 while ( element ) {
462469 if ( this . observedElements . has ( element ) ) {
463470 targets . add ( element )
464471 break
465472 }
466- if ( ! element . parentElement ) break
467473 element = element . parentElement
468474 }
469475 } )
470476
471- // Call callbacks for affected elements
472477 targets . forEach ( ( element ) => {
473478 const callback = this . observedElements . get ( element )
474479 if ( callback ) callback ( )
475480 } )
476481 } )
477482 }
478483
484+ private ensureMutationObserverConnected ( ) : void {
485+ if ( this . isMutationObserverActive ) return
486+ if ( this . observedElements . size === 0 ) return
487+ if ( ! document . body ) return
488+
489+ this . mutationObserver . observe ( document . body , {
490+ childList : true ,
491+ subtree : true ,
492+ attributes : true ,
493+ characterData : true ,
494+ } )
495+ this . isMutationObserverActive = true
496+ }
497+
498+ private disconnectMutationObserver ( ) : void {
499+ if ( ! this . isMutationObserverActive ) return
500+ this . mutationObserver . disconnect ( )
501+ this . isMutationObserverActive = false
502+ }
503+
479504 public static getInstance ( ) : ElementObserverManager {
480505 if ( ! ElementObserverManager . instance ) {
481506 ElementObserverManager . instance = new ElementObserverManager ( )
@@ -490,55 +515,18 @@ export class ElementObserverManager {
490515
491516 this . observedElements . set ( element , callback )
492517 this . resizeObserver . observe ( element )
493- if ( ! this . pendingReconnectFrame ) {
494- this . mutationObserver . observe ( element , {
495- childList : true ,
496- subtree : true ,
497- attributes : true ,
498- characterData : true ,
499- } )
500- }
518+ this . ensureMutationObserverConnected ( )
501519 }
502520
503521 public unobserve ( element : HTMLElement ) : void {
504522 if ( ! element ) return
505523 this . observedElements . delete ( element )
506524 this . resizeObserver . unobserve ( element )
507525 this . pruneDetachedObservedElements ( )
508- this . mutationObserver . disconnect ( )
509526
510527 if ( this . observedElements . size === 0 ) {
511- if ( this . pendingReconnectFrame ) {
512- window . cancelAnimationFrame ( this . pendingReconnectFrame )
513- this . pendingReconnectFrame = undefined
514- }
515- this . mutationObserver . disconnect ( )
516528 this . resizeObserver . disconnect ( )
517- return
518- }
519-
520- if ( ! this . pendingReconnectFrame ) {
521- this . pendingReconnectFrame = window . requestAnimationFrame ( ( ) => {
522- this . pendingReconnectFrame = undefined
523-
524- // MutationObserver has no per-element unobserve, so we reconnect once per frame.
525- this . pruneDetachedObservedElements ( )
526-
527- if ( this . observedElements . size === 0 ) {
528- this . resizeObserver . disconnect ( )
529- return
530- }
531-
532- this . observedElements . forEach ( ( _ , el ) => {
533- if ( ! document . contains ( el ) ) return
534- this . mutationObserver . observe ( el , {
535- childList : true ,
536- subtree : true ,
537- attributes : true ,
538- characterData : true ,
539- } )
540- } )
541- } )
529+ this . disconnectMutationObserver ( )
542530 }
543531 }
544532}
0 commit comments