Safari has no scroll chaining, why do we?

445 views
Skip to first unread message

Rick Byers

unread,
Aug 29, 2015, 8:12:22 AM8/29/15
to input-dev, Tim Dresser, Alexandre Elias
Simon Fraser just pointed out (and I confirmed) that neither mobile Safari nor desktop Safari ever do scroll chaining.  When you start scrolling the element to be scrolled is uniquely determined and doesn't change for the duration of the scroll gesture (finger lifted from touchscreen or touchpad - not sure what they do for physical wheel but either way doesn't matter).

I don't know how I missed (or forgot) this.  Personally I think this might be a better user experience?  The fact that, in all my use of Safari, I've never been annoyed by this suggests to me that maybe it's not unreasonable.  There are certainly cases where scroll chaining gets in the way.  If we decided we never wanted scroll chaining, it would simplify a lot of things in our implementation (eg. scrolling in OOPIF scenarios).  Further if we got to a point where all browsers discouraged use of scroll chaining, we could probably simplify the scroll customization APIs (eg. kill the distributeScroll hook).

Thoughts?
   Rick

Alexandre Elias

unread,
Aug 29, 2015, 3:22:17 PM8/29/15
to Rick Byers, input-dev, Tim Dresser
Scroll chaining is particularly important in the case where the element is not currently scrollable on that axis.  A representative example is the horizontally scrollable carousel on http://m.theverge.com/.  If you happen to start a vertical scroll on it, it would be bad for nothing to happen.

(Likewise the special angle-based suppression logic we have around scroll chaining is for this case.  When the user does intend to scroll that carousel horizontally, we don't small vertical portions of the scroll to cause the entire screen to jitter vertically.)

We also wanted to make it impossible to be trapped on a large scrollable element that fills the viewport.  So even if it's scrollable on all axes, it should be possible to escape up to the parent scroller once it's reached the limit on one of them.


A possible simplification of scroll chaining still meeting those use cases would be to lock scrolling to the element scrolled by the first delta.  At a quick test, this is what iOS Safari seems to do.  We have a lot of logic like that in the code today created for other use cases like wheel/touchpad and maybe we could try enabling it for touch.  It would have the virtue of allowing us to eliminate the chaining-specific angle-based suppression.  I'm not sure exactly how much it would help simplify OOPIF though (the first delta would still need to bounce across frames).

Alexandre Elias

unread,
Aug 29, 2015, 4:14:40 PM8/29/15
to Rick Byers, input-dev, Tim Dresser
Thinking about it more, it's also related to a philosophical difference with iOS Safari: Safari is very axis-lock-happy whereas we want to allow diagonal scrolls mid-gesture changes if the user's express intent seems to be that.  The most important example of this is the vertical/horizontal locking behavior within a single scrollable area, but we were also thinking along the same lines when we did scroll chaining.

If I start a horizontal gesture on that theverge.com div, then switch to a sharp upwards scroll, it feels wrong for the browser to totally ignore that input.  Likewise if I'm pinched zoomed in on an desktop site and do a diagonal gesture that happens to start on a scrollable div, it risks getting "stuck" on it.  The scenarios a bit niche but it's one of those polish details I'd prefer to keep if we can.  Particularly since I'm not convinced it would truly simplify OOPIF or scroll customization architecture to only try to chain on the first scroll delta as opposed to all of them (the architecture *would* be simpler if we could decide the scrolling layer on ScrollBegin, but we can't).

There's one more behavior that comes to mind we could change.  Currently, the scrolling layer always remains the first one scrolled, and the deltas are chained one-by-one.  So in one gesture, I can scroll the child, the parent, then the child again.  It's a bit weird.  It might be fine and possibly a UX improvement to switch to transfer the CurrentlyScrollingLayer to the parent after one delta scrolled the parent.

Rick Byers

unread,
Aug 29, 2015, 4:24:44 PM8/29/15
to Alexandre Elias, input-dev, Tim Dresser
I'm definitely not suggesting that we always target the inner-most node.  In addition to the 2-axis scenario you describe, that would also mean you could get stuck in the document when a scrollable div fills the viewport.  I'm suggesting only doing exactly what Safari does - lock the scroller on the first gesture.

You said it would be simpler if we can lock on GestureScrollBegin.  Actually we can do that.  We had to add deltaXhint / deltaYhint to WebGestureEvent for touch-action, so on GestureScrollBegin we know exactly which direction the scroll will go so have everything we need to fix the right target.

Also this definitely would simplify OOPIF.  We don't think we can afford to have EVERY GestureScrollUpdate jump around between renderers, so we've contemplated building some sort of heuristic (eg. re-compute the target every direction change).  With "scroll latching" (as Safari calls the feature), we can compute the target just at GestureScrollBegin time (where we have a bit more time to respond without the user noticing than on GestureScrollUpdate).

I agree we'd want to retain diagonal scrolling.  And maybe it wouldn't feel good for direction change not to trigger any chaining.  But let me ask this: in all your use of Safari, have you been bothered by this missing functionality?

Rick

Rick Byers

unread,
Aug 29, 2015, 4:28:19 PM8/29/15
to Alexandre Elias, input-dev, Tim Dresser
I just checked Firefox mobile and it behaves the same as Safari.  Can someone check Edge or IE (test page)?  I'm travelling without most of my devices...  We're not the odd one out here, are we?

From a practical perspective, if we're the only browser doing this, it's going to make it harder to standardize scroll customization.  It certainly adds a bunch of complexity that engines without this will have no interest in supporting.

Rick

Alexandre Elias

unread,
Aug 29, 2015, 6:39:30 PM8/29/15
to Rick Byers, Timothy Dresser, input-dev

OK, I think you convinced me.  Feel free to land the behavior change now.  If no one complains, we can gain confidence in tying architecture and APIs to it going forward.

Kartikaya Gupta

unread,
Aug 29, 2015, 9:49:05 PM8/29/15
to Rick Byers, Timothy Dresser, inpu...@chromium.org, Alexandre Elias

The scrolling code in Firefox for Android indeed behaves this way, but it will change pretty soon. On Firefox OS we do have scroll chaining (we call it scroll handoff), and the scrolling implementation that is being used there will be coming to desktop and Android in the near future.

Of course, it may be that once it arrives people will want to disable the chaining behavior but as it stands we do plan on keeping it. The reasons are the ones already mentioned in this thread, particularly the whole "getting stuck inside a giant iframe" thing.

Cheers,
kats

To unsubscribe from this group and stop receiving emails from it, send an email to input-dev+...@chromium.org.

Rick Byers

unread,
Aug 30, 2015, 5:21:28 AM8/30/15
to Kartikaya Gupta, Timothy Dresser, input-dev, Alexandre Elias
Alex: Thanks, filed a bug for this for gesture scrolls.  I agree we should try this first (should be easy I think) and get feedback.  If we decide we're going to stick with it, then we can do the additional (somewhat harder) work for touchpad and wheel.

Kats: Thanks for the details!  When you say "getting stuck inside a giant iframe", can the user still get out by lifting their finger and scrolling a second time?  That's the behavior on Safari and it seems pretty good to me (just a little annoying if you were at say scrollTop<5 in the iframe, takes two swipes to move the document).  Perhaps Firefox's 'wheel transaction' timer makes this more annoying in the wheel case?

Rick

Kartikaya Gupta

unread,
Aug 31, 2015, 12:08:10 AM8/31/15
to Rick Byers, inpu...@chromium.org, Alexandre Elias, Timothy Dresser

This is the scenario I had in mind: let's say you have a document with an iframe. The iframe is already scrolled up to the top and the user puts their finger on it. At this point our implementation needs to decide which scrollframe is going to be the active one - the iframe or the parent document. It sounds like with the Safari model it could be either - if the user moves their finger upwards it would be the iframe that scrolls downwards, but if the user moves their finger downwards it would be the outer document that scrolls upwards.

In our implementation it's simplest to start off with the inner scrollframe as active and then allow chaining to let the outer document scroll if the user moves their finger downwards. Changing the implementation to defer the decision until the user starts moving their finger would be quite hard; as a result we need to at least have the code to implement chaining. We *could* only allow scroll chaining if the scroll offset starts at 0 (or at other end, the max allowed scroll offset) but we never really saw a need to add that extra complexity and nobody ever asked for it.

The wheel transaction timeout is pretty independent of all this, and doesn't carry over to touch input.

Hope that helps (and that it makes sense).

Cheers,
kats

Timothy Dresser

unread,
Aug 31, 2015, 2:12:36 PM8/31/15
to Kartikaya Gupta, Rick Byers, inpu...@chromium.org, Alexandre Elias, Ian Vollick
This proposal doesn't get rid of the need for a scroll mediation primitive, it just changes the kind of required primitive. Instead of "how much scroll does each element consume?" we now need to answer the question "which element is consuming scroll?" I'm not sure this would simplify scroll customization.

We could stick with the current scroll customization proposal, but replace distributeRoutedScrollIntent with an acceptsScroll method, which would take a starting delta, and return true if that element should own the scroll, and false otherwise. I don't think this is actually much simpler than our existing scroll customization proposal.

The biggest opportunity for simplification in scroll customization if we get rid of scroll chaining would be to switch to a few simple synchronous events. There were some disagreements on when the DOM event system should be used last time we proposed this.

The only required mediation logic in this context is deciding which element should own the scroll. A somewhat weak proposal: this could be accomplished with a pair of events. If a native scroller can scroll, then the event receives a 'startingScroll' event, which, if preventDefaulted, stops the native scroller from owning the scroll. An element with a beforeScroll event handler, or a native scroller which can't scroll receives an 'ignoringScroll' event, which if preventDefaulted causes the element to own the scroll.

Then for each scroll delta, we'd dispatch a synchronous beforeScroll event, which, if preventDefaulted, would prevent the native scroll behavior from occurring.

This solution would be a lot simpler, but would probably meet pushback due to the abuse of DOM events. (There was also some talk of this being impossible due to the scroll chain being independent from the DOM hierarchy, but I couldn't come up with a case where the scroll chain isn't a subset of the DOM tree.)

I spent some time trying to come up with use cases on the web which require scroll chaining. The best example I've come up with is pull-to-refresh where the contents are in an iframe (perhaps to achieve performance isolation.) When the user has scrolled to the top of the iframe, and continues scrolling, we need to smoothly transition to pulling down the linen in the outer frame. I don't think this specific example is enough to indicate that we still need distributeRoutedScrollIntent in scroll customization, but there may be more like it.

Tim

Rick Byers

unread,
Sep 3, 2015, 7:38:59 AM9/3/15
to Kartikaya Gupta, input-dev, Alexandre Elias, Timothy Dresser
Thanks Kats, I understand.  We both agree the scenario you describe must be addressed somehow.  

I think we'd have some implementation challenges solving this scenario (for both touchscreen and touchpad) without also causing scroll chaining.  But I think we'll at least look into it.  I'd prefer to make this decision primarily based on what the right user experience is.  

Rick

Rick Byers

unread,
Sep 3, 2015, 7:41:21 AM9/3/15
to Timothy Dresser, Kartikaya Gupta, input-dev, Alexandre Elias, Ian Vollick
Thanks Tim.  I agree we probably don't want to revisit the whole DOM event model.  I suggest we don't worry too much about the impact of this choice on scroll customization at the moment - I don't think scroll customization would be the thing that would sway us either way.

Instead, if we can make the user experience better or (without hurting user experience) make the engine simpler, then I think it's worthwhile.

Kartikaya Gupta

unread,
Sep 3, 2015, 11:37:20 AM9/3/15
to Rick Byers, input-dev, Alexandre Elias, Timothy Dresser
(Re-sending since I accidentally didn't reply-all)

Sounds good - let me know how things turn out. I'm happy to revisit
this decision on our side if you find that some other behavior results
in a noticeably better UX.

Cheers,
kats
Reply all
Reply to author
Forward
0 new messages