In Safari and iOS Safari/Chrome (but not desktop Chrome), if CSS scroll snapping is enabled, then a fast scroll ("flick" on mobile) will scroll to the end or beginning of the react-window list, even if the list is thousands of items long.
The problem is caused by a difference in how Blink (desktop Chrome) vs. WebKit (Safari, iOS) handles snap-scrolling past the last scroll snap position. Blink will refuse to scroll past the last scroll snap position so that react-window has time to populate new items in front of where the user is scrolling. WebKit, however, if the scroll is fast enough, will immediately scroll to the very end of the container before react-window has a chance to place new items in front of the user's scroll position.
Repro
Expected: normal scroll
Actual: scrolls all the way to the end/beginning of the 1000-item list! Note that this is the same behavior seen in the "HTML sparse" list when scrolling past the last item in the list.
I'm continuing to investigate this, but if anyone has seen this problem before or has insights into how to fix or work around it, let me know!
I also filed ~https://github.com/w3c/csswg-drafts/issues/4111~ _(UPDATE: discussion moved to https://github.com/w3c/csswg-drafts/issues/4037)_ to ask about whether Chrome's or WebKit's behavior is correct according to the standard. While I was there I asked for suggestions for how we can get the Chrome behavior on Safari.
I've skimmed the open WebKit bugs related to scroll snapping. I didn't see any obvious bugs that suggest this is a bug as opposed to expected behavior for WebKit.
After a long day of trying to find a workaround, I didn't make much progress. I found that setting scroll-snap-type: x proximity instead of scroll-snap-type: x mandatory fixes the problem on desktop Safari, but iOS browsers are still broken. (And using proximity makes desktop Chrome worse.) So that's not really a viable fix.
Unless anyone has other suggestions, I think the next step will be to give up on CSS snapping in Safari and iOS and use a polyfill like https://github.com/PureCarsLabs/css-scroll-snap-polyfill, or to roll my own solution. I'm leaning towards the latter.
If I end up rolling my own fix, it'd probably go something like this:
scrollToscrollTo will trigger more onScroll events and (per #228) there's not a reliable way in current react-window to know if an onScroll was user-initiated or programmatic.@bvaughn - I see that react-window already tracks isScrolling in component state. Seems wasteful (in complexity and perf) to add another scroll handler and debouncer to duplicate what react-window is already doing internally. Would you be open to a PR that adds an onScrollEnd event to the four react-window components? This would be helpful in use-cases beyond scroll snapping, like when you want to update another component after a list is done scrolling. I can't promise that I'll have time to make a PR for this, but if I do have time then I wanted to know if you think it'd be helpful.
I was trying a similar configuration as @justingrant and run into the same problem. Too bad because react-window with scroll-snap would be great – but it is too buggy on Safari.
It doesn't seem that easy to create a polyfill either...
I found adding -webkit-overflow-scrolling: touch; fixed a similar issue for me with scroll-snap-type: x mandatory might help here?
I had the same problem with horizontal scrolling in iOS devices and found out something that might save some time for you:
apparently, the snap scrolling only work on direction: ltr and not on "rtl" (in iOS devices),
and also the scrolling stop working altogether when the parent has flex-direction: row-reverse.
hope it helps someone.
Just ran into that myself on the RTL issue and landed here. The webkit bug for that appears to be https://bugs.webkit.org/show_bug.cgi?id=193671.
Most helpful comment
I had the same problem with horizontal scrolling in iOS devices and found out something that might save some time for you:
apparently, the snap scrolling only work on
direction: ltrand not on "rtl" (in iOS devices),and also the scrolling stop working altogether when the parent has
flex-direction: row-reverse.hope it helps someone.