w3c / dom

Moved to https://dom.spec.whatwg.org/
60 stars 18 forks source link

Browsers should offer an asychronous Layout API #162

Open Schepp opened 7 years ago

Schepp commented 7 years ago

Hey everyone,

Right now I'm working quite a lot with the Intersection Observer API and I like it a lot. I especially like the idea of events being pushed to me in an efficient way instead of me having to poll them.

Sometimes though I wish I could do a call on an element to ask for its current intersection state, e.g. when I have a slider element and I want to know which elements are currently visible within the clipping area. I could implement this by observing all of them constantly via Intersection Observer and somewhere keeping track of their current state. But even better suited would be a DOM method to ask for the intersection metrics. I do understand that W3C is not offering such an API as it would trigger reflow/layout in the browser and would therefore be a perf hog. But what about offering such a method in the form of an asynchronous, e.g. promise based API? e.g.

elem.getIntersectionState({
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0,
}).then((result) => {
  /* do whatever you need to */
});

But then I thought, since we are at it, would it not also make sense to offer async variants of elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent, elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight, elem.getClientRects(), elem.getBoundingClientRect(), elem.scrollWidth, elem.scrollHeight, elem.scrollLeft and elem.scrollTop?

The most obvious idea here might be to offer new async methods for each of the above properties. And that's certainly not a bad idea. But the thing is that in everyday use cases one often needs to factor in multiple of these layout metrics before executing some custom code. Therefore it would be quite important that all of the async responses represent the same layout moment in time. And this means we'd need one big mother of all layout methods asynchonously returning all of the above metrics at once. E.g.:

const options = {
  intersections: [{
    root: document.querySelector('#scrollArea'),
    rootMargin: '0px',
    threshold: 1.0,
  }],
}

elem.getLayoutState(options).then((result) => {
  /* standard metrics */
  console.log(result.offsetTop);

  /* intersections */
  result.intersections.forEach(entry => console.log(entry.isIntersecting));
});

Please note that since one may be interested in many different intersection relations, I made the intersection relevant part of the above code an array.

The question now is: what do you W3C people think of this? Is there already something similar in the making? Is the idea somehow flawed? Looking forward to your thoughts!

nuxodin commented 7 years ago

Would something like this make sense?

requestLayoutFrame(()=>{
    el.offsetHeight // very fast  
    el.style.height = '2em' // throw
})
Schepp commented 7 years ago

@nuxodin That would be fine to me as well, although we would still need a solution for getting intersection states.

rikschennink commented 7 years ago

In my tests this works, as offsetHeight has been calculated for the frame. Requesting it does not trigger a reflow (as long as you don't invalidate the DOM).

requestAnimationFrame(() => {
    el.offsetHeight // very fast
})
Schepp commented 7 years ago

Thank you @rikschennink! That basically what FastDOM does. I still find it a bit too much code, TBH, like with the above requestLayoutFrame() idea, but I could live with it. What still remains unsolved is where to get intersections from.

rikschennink commented 7 years ago

@Schepp I should've read the post a little bit better, I misunderstood.

Would be nice to have a generic "get everything about the elements" layout method, it seems the information is readily available at the start of the frame.

elem.getLayoutState(options) does not really sound like an async action though, maybe elem.requestLayoutState(options)?

Wouldn't mind a sync version as well. If called at the start of a frame that would still be very useful.

Schepp commented 7 years ago

@rikschennink elem.requestLayoutState(options) sounds like music in my ears! :)

chaals commented 7 years ago

I suggest that you propose this in the repository for IntersectionObserver, and / or the Web Platform Incubation Community Group, to get some traction among implementors and more support from users.

Before a feature request like this gets folded into the DOM recommendation we need to demonstrate interopability and uptake, which is what WICG is designed to do. (A few other pieces of the work include test cases, and a good explanation of how it works...)