Overscroll & Pull to Refresh

in Chrome for Android


Overscroll Glow

The overscroll glow effect on Android is triggered, per axis, under the following conditions:

  • The viewport is scrollable (non-zero scroll affordance)
  • The viewport is at an extreme scroll offset (e.g., the origin)
  • The viewport receives a scroll delta input in a non-scrollable direction[1]

Under such conditions, the unused, viewport-targeted scroll delta in the non-scrollable direction will be provided as an impulse to the edge glow effect. Note that content can prevent the overscroll effect from occurring by:

  • Consuming (via preventDefault) the generative touch events with a touch handler (including the synchronous touch events that are generated when a scroll sequence overscrolls, see throttled async touch events)
  • Using a combination of scrollable subdivs and overflow style properties (effectively preventing the viewport from receiving the scroll)[2]

Pull-to-refresh Behavior

Pull-to-refresh in Chrome for Android (shipped in m41) augments the overscroll glow as one of the “default actions” for a touch sequence. The effect’s activation is slightly more restrictive in its initial conditions:

  • The scroll sequence must start when the page has a y-axis scroll offset of 0 (it’s at the top)
  • The initial scroll delta must be upward (user motion downward), in the non-scrollable direction
  • The accumulated overscroll reaches a sufficient minimum threshold, and is beyond this threshold when the touch sequence ends.

Transitioning from scrolling (or flinging) into overscroll continues to yield the overscroll glow, partially mitigating accidental refreshes. The refresh effect and action can be prevented by similar mechanisms noted for the overscroll glow effect, and is be purely cosmetic (not deforming or translating the content in any fashion). GMail’s pull-to-refresh provides a simple, cosmetic visual feedback model.

Preventing the pull-to-refresh effect

The default action of the pull-to-refresh effect can be effectively prevented by doing any of the following :

  1. Applying “touch-action: none” to touch-targeted elements, where appropriate, disabling default actions (including pull-to-refresh) of the touch sequence.
  2. Applying “overflow-y: hidden” to the body element, using a div for scrollable content if necessary[3].
  3. preventDefault’ing some portion of the touch sequence, including any of the following (in order of most disruptive to least disruptive):
  1. The entire touch stream (not ideal).
  2. All top overscrolling[4] touchmoves.
  3. The first top overscrolling touchmove.
  4. The first top overscrolling touchmove only when 1) the initial touchstart occurred when the page y scroll offset was zero and 2) the touchmove would induce top overscroll.
  1. Disabling the effect locally via chrome://flags (disable-pull-to-refresh-effect).

Note that the pull-to-refresh effect will never activate if any scrolling occurs before the overscrolling motion, e.g., if the user first scrolls down, then back up, or if the page scroll offset is 0 but has a scrolling div that the user scrolls up before overscrolling.

See this simple page for an example of how to suppress the pull-to-refresh effect, including how to suppress pull-to-refresh but preserve the overscroll glow.

[1] Currently, we only detect overscroll for scrolls hitting the compositor thread, crbug.com/420207.

[2] This could be considered a bug, but some web devs use such a path explicitly to prevent the default top controls hiding behavior.

[3] This solution may change at some point in the future, but for the time being it also serves to disable omnibox hiding and some other default behaviors associated with body scrolling.

[4] Overscrolling touchmoves in this sense means touch events where 1) the page y scroll offset is zero, and 2) the touch event’s primary motion direction is downward (i.e., touch event coordinates are *increasing* and the user is logically overscrolling the page top).