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.
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
scrollIntoView()
method return PromisescrollIntoView()
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.
Most helpful comment
Is there any chance of
element.scrollIntoView
andwindow.scroll
returning a promise that resolves when the scroll animation has ended? Or is that going to break backwards compatibility?Applied to
element.scrollIntoView
Applied to
window.scroll