A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

Idea: `ClientRectObserver`

trusktr
2022-12-28

Problem

It is essentially impossible to observe an element bounding client rect (el.getBoundingClientRect()) without resorting to a polling solution (for example an animation frame loop).

Trying to implement it without polling is futile. For example see the question and answers here:

It might be possible to ponyfill this hypothetical ClientRectObserver API without polling, but it will be very convoluted:

  • Use InterSection observer (only with ancestors of the target element) and scroll events (on all scrollable ancestors of the target element) to detect possible position changes of the target element’s bounding rect.
  • Use MutationObserver to observe every single possible DOM mutation that could ever change the target element’s bounding client rect
    • style changes to any ancestors, and siblings within layouts like flexbox, etc
    • stylesheet changes to all style and link elements
    • forget about CSSOM.
  • position:fixed tricks that break with containing block changes
  • etc

You get the picture: practically impossible, or at best DOOM ultra-nightmare code.

Solution

A new ClientRectObserver API would allow us to simply observe an element’s bounding client DOMRect. It would be something like the following:

const element = document.querySelector('.someElement')

const clientRectObserver = new ClientRectObserver((entries) => {
  // any time the client rect could ever change, log it.
  for (const entry of entries) {
    console.log(entry.target.getBoundingClientRect())

    // similar to ResizeObserverEntry/IntersectionObserverEntry:
    console.log(entry.contentRect)
    console.log(entry.boundingClientRect)
  }
})

and done!

Why

There are plenty of reasons to depend on client rects:

  • implement new layouts, f.e. anchoring one element on the edge of another
  • re-using DOM layout (in hidden elements) to apply to canvas renderings
  • rendering things on a WebGL canvas based on DOM element positioning, for example rendering multiple “canvases” using a single overlaid canvas (note, there is a limit of 32 webgl contexts in Chrome as of this writing, so this is a requirement when we need to render multiple little scenes)
  • etc

Having this API would not only make things easier for web devs, but help them avoid bugs. I’ve experience bugs with polled bounding rect values and code running before the polled value is up to date. Ideally any bounding rect value would be updated exactly when it should be, and all other code would always have the latest value without worrying about code ordering.

Calling getBoundingClientRect every single time it is needed in an app solves the issue, but introduces a heavy performance hit because browsers (f.e. Chrome) currently re-calculate bounds on every call.

Marckon
2023-03-09

I vote for you! Really look forward to something that can help me locate an element based on another element. Current UIs like tooltip, suggestion, or dialog are all likely to deflect their target when scrolling or target’s size changing happens.

simevidas
2023-03-12

For size changes, there is ResizeObserver, but for position changes, there exists no API, and your proposal would be that API. Do I understand this correctly?

trusktr
2023-04-18

@simevidas correct! Both ResizeObserver and IntersectionObserver do not handle this case.

A subset of the solution is possible with IntersectionObserver, but it is cumbersome and brittle (shown in the linked StackOverflow question).

ClientRectObserver would be a superset of ResizeObserver, because resize changes an element client rect. ResizeObserver does not detect changes in position where the size is the same between positions.