Csswg-drafts: [cssom-view-1] Provide onAnimationEnd callback in scrollIntoView options

Created on 19 Mar 2019  路  4Comments  路  Source: w3c/csswg-drafts

Spec

Right now you can't really tell if scrollIntoView scroll animation has finished nor you can't control the speed of the scroll and it might differ with each browser. So if you want to perform some action right after the animation has finished there's no determined way to do that. Having an onAnimationEnd callback as an initializer option would solve this problem:

Element.scrollIntoView({
  onAnimationEnd: () => Element.focus()
})

Why we would ever need that?

Imagine that you have some input you would like to focus that's out of the viewport boundaries right now, but you would also like to have a smooth scrolling animation to that input. If you execute focus before scrollIntoView then you'll get no scroll animation, because browser will
already scroll to that input without animation.

cssom-view-1

Most helpful comment

Is there any chance of element.scrollIntoView and window.scroll returning a promise that resolves when the scroll animation has ended? Or is that going to break backwards compatibility?

Applied to element.scrollIntoView

element.scrollIntoView({ behavior: 'smooth', block: 'start' }).then( scrollEvent => {
  console.log('The browser has finished scrolling')
})

Applied to window.scroll

window.scroll({ top: 0, left: 0, behavior: 'smooth' }).then( scrollEvent => {
  console.log('The browser has finished scrolling')
})

All 4 comments

Methinks this should have the same solution as #156 - CSS Snap Points: Event Model Missing

Is there any chance of element.scrollIntoView and window.scroll returning a promise that resolves when the scroll animation has ended? Or is that going to break backwards compatibility?

Applied to element.scrollIntoView

element.scrollIntoView({ behavior: 'smooth', block: 'start' }).then( scrollEvent => {
  console.log('The browser has finished scrolling')
})

Applied to window.scroll

window.scroll({ top: 0, left: 0, behavior: 'smooth' }).then( scrollEvent => {
  console.log('The browser has finished scrolling')
})

The particular scenario mentioned by @CyberAP can be solved as follow:

CSS:
html { scroll-behavior: smooth; }

JS:

element.scrollIntoView(..params);
element.focus({preventScroll:true});

Without the preventScroll parameter, you are subject to browser quirks as below:

Firefox
Scrolls to the correct position (meaning, element.scrollIntoView) yet abandons smooth scrolling, even though the browser supports it. Not great, but at least the scroll position is correct.

Safari/webkit
Same as Firefox yet would not scroll smoothly in any case, as it's not supported. Not great, but at least the scroll position is correct.

Chrome
Chrome is the big offender here. It seems to cancel the scrollIntoView action altogether, instead does the smooth scroll from element.focus(), CSS based, and scroll position logic is based on that. In practice, this often means a wrong scroll position.

Other scenarios
Whilst preventScroll solves this particular scenario, I still very much agree we need a callback. If you need to do anything else after scrolling completes (say, run an animation), there's no robust way to do that right now.

There is another use case: when scrolling to particular element it is common that one must take in an account fixed-position headers. Unfortunately the very widespread solution is to have resizable fixed headers. E.g. tall header in the initial position and minimal header after a page was scrolled a bit.

That poses the problem with adjusting the target scroll position as the header changes size.

While one can easily use ResizeObserver to watch for header changes and adjust the target scroll offset accordingly there is no way to know when to stop and disconnect ResizeObserver after scrolling finished.

Ideally I would love to see

  1. scrollIntoView() method return Promise
  2. scrollIntoView() method honoring real-time changes to CSS scroll-padding property while scrolling is in progress

That way one can use ResizeObserver to update CSS properties and after scrolling finishes disconnect it.

Was this page helpful?
0 / 5 - 0 ratings