Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which elements are observed by a given ResizeObserver?

Tags:

javascript

css

Summary: Is there a way to get a list of the elements currently observed by a given ResizeObserver?

Assume an existing ResizeObserver that observes some elements

let observer = new ResizeObserver(_ => _.forEach(_ => console.log(_.contentBoxSize[0])));
observer.observe(document.getElementById('my-element-1');
observer.observe(document.getElementById('my-element-2');

Is there a way to get the elements the ResizeObserver is observing like

observer.getObservedElements() === [ // this API does not exist
  document.getElementById('my-element-1'),
  document.getElementById('my-element-2')
];

Use cases that I would have used such an API (there are probably more):

  • Consider a single generic ResizeObserver, shared in the whole application, that performs the same action for all observed elements (e.g. fire a framework-internal "element resize" event). No-one knows what elements are already observed. But I have to make sure that no already-observed element is observed again (otherwise I would get "resize events" twice).
  • For testing purposes I need to assert that an element is observed. Mocking ResizeObserver would be a lot of effort and would probably interfere with other components that need the real ResizeObserver.

Am I missing something from the MDN docs or the CSSWG draft? I only see observe(), unobserve() and disconnect().

like image 300
Dominik Schreiber Avatar asked Oct 30 '25 21:10

Dominik Schreiber


1 Answers

The problem with such an API is that it would keep the observed elements from being Garbage Collectable, even though the specs are still not clear on this case, implementations (at least Chrome) do currently unobserve target Elements that aren't referenced anywhere else. If they had to expose which elements are being observed, they would need to keep it in the list with the potential memory leak that follows.

Though for your own testing case, you could probably overwrite the observe and unobserve methods to update a Set attached to the Observer:

(function() {

  const targets = new WeakMap();
  Object.defineProperty( ResizeObserver.prototype, "targets", { 
    get() {
      let slot = targets[ this ];
      if( !slot ) {
        slot = new Set()
        targets[ this ] = slot;
      }
      return slot;
    }
  } );

  const original_observe = ResizeObserver.prototype.observe;
  const original_unobserve = ResizeObserver.prototype.unobserve;
  ResizeObserver.prototype.observe = function( elem, ...args ) {
    this.targets.add( elem );
    return original_observe.call( this, elem, ...args );
  };
  ResizeObserver.prototype.unobserve = function( elem, ...args ) {
    this.targets.delete( elem );
    return original_observe.call( this, elem, ...args );
  };

})();

const observer = new ResizeObserver( (entries) => console.log( "size changed" ) );

observer.observe( document.getElementById( "target1") );
observer.observe( document.getElementById( "target2") );
observer.unobserve( document.getElementById( "target1") );

console.log( [...observer.targets] ); // #target2
[id^="target"] { border: 1px solid; height: calc(50% - 4px); }
.resizeable-container {
  width: 50vw;
  height: 50vh;
  resize: both;
  overflow: auto;
}
<div class="resizeable-container">
  <div id="target1"></div>
  <div id="target2"></div>
</div>

Also, regarding your first use-case, you don't have to worry about observing multiple times the same Element, it's already handled in the specs: if you call observe with an Element that is already in the observer's [[observationTargets]] internal slot, it will first get removed and then re-added with the new options if any.

like image 118
Kaiido Avatar answered Nov 01 '25 11:11

Kaiido



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!