[css-snappoints] Always snapping to a mandatory point

Rob and Majid both recently brought up questions about the invariant in the spec of always ensuring the scroll offset satisfies a snap point when mandatory snap points are in use, so I thought it would be worthwhile to split that discussion off into a separate thread for more in-depth discussion.  I'll do similar for the other issues that have come up since my last rollup email.

> I would argue that there should be a way for programmatic scrolling to
> evade "snap-points" when necessary. Perhaps evading snap points should be
> default for programmatic scrolls with an option to opt in to snap points!
> When an application uses a programmatic scrolls to scroll to a specific
> offset it actually means to scroll to that offset and snap points should
> not interfere with that.

and

> However, that conflicts with the spec text "If the content changes such
> that the visual viewport would no longer rest on a snap point (e.g. content
> is added, moved, deleted, resized), the scroll offset must be modified to
> maintain this guarantee." It doesn't make sense to allow some DOM APIs to
> violate that invariant but require other content changes to preserve it. If
> a script scrolls away from a snappoint, and then there's an insignificant
> DOM change, what would we do? I suggest we just remove that spec
> requirement; I think it's hard to implement and is likely to produce
> undesirable behavior anyway.

and

> If we ensure there is an API for pragmatically causing snap then
> applications can use it to cause snap when desired after any scripted
> scrolling or any changes that may update layout. This is backward
> compatible and allows developers to create the experience they want without
> needing to re-implement snap themselves. Perhaps something like:
> Element.scrollTo(x,y, 'snap').

This requirement is in direct response to feedback we received that our implementation of the API was difficult to use because there was no good way to ensure a mandatory snap point was always satisfied.  If you're able to try building some basic scenarios with the IE11 implementation you'll quickly see how this is painful to implement even in the basic 100% interval case.

The imperative API approach is not sufficient to satisfy the scenario; it just pushes the requirement to the author rather than the implementor.  This is a requirement that implementors should take on rather than authors, because:
1) The author would need to understand and arbitrate between all input types and potential causes of content change to ensure they do not force a snap while the user is still interacting with the scroller, but does snap as soon as a rest state is reached.  If a new input type is introduced (e.g. the author didn't think about touch) then the control likely breaks.
2) The author currently has no way of knowing (esp. interoperably across browsers) when scrollbar-based scrolling has reached a steady state, e.g. when the user has "let go" of the scrollbar gripper.  Thus it's difficult to know when it's safe to call an imperative API to force a snap.
3) Even for input types that have better eventing, this will require the developer to register for all input events of all types to track when the scroller has reached a rest state.  Probably mutation observers as well unless they can constrain their control's layout to avoid reaching an invalid scroll offset.
4) The author does not know which snap point is appropriate to snap to without doing a large amount of work to track the currently snapped element and keep that up to date.  Consider example 1 from the spec [1], and suppose the user is currently viewing image 5 (thus the scrollLeft is 2000).  If the scroller and images are resized to 1000px wide (perhaps in response to the user maximizing their browser window) then image 3 is suddenly in view rather than image 5.  The developer would have had to track that image 5 was the currently snapped image, detect that something caused it to move, and called the imperative API to re-snap to the correct image.

Actually, with regard to point #4, the invariant should be clarified in the spec that the *same* snap point must be snapped to (if it still exists) when content changes, not just any snap point.

If evading a snap point is a goal with a script-based scroll (is there a supporting scenario for this?) then it's inappropriate to use mandatory-type snap points since it's obviously not mandatory to snap.  Proximity snap points are likely the appropriate type in that case.  Mandatory signifies "the user will see a view that is unacceptable in the rest state if they are not snapped to a mandatory point".  Proximity signifies "the user will see a more-optimal view if they are snapped to a proximity point".  These definitions are intentionally agnostic of what mechanism was used to execute the scroll because they're effectively describing valid/optimal views on the content, rather than directly describing scrolling mechanism behaviors.

Agreed that this requirement isn't the easiest, but it's also where the feature holds value to authors so they don't have to implement it themselves.

Thanks,
-Matt

[1] https://drafts.csswg.org/css-snappoints/#example-7c8cd7ae

Received on Wednesday, 29 July 2015 19:35:40 UTC