How Pull to Refresh Works in iOS 15 Safari
With the release of iOS 15, Apple introduced a native pull-to-refresh gesture to Safari, bringing the mobile browser in line with the behavior of native iOS applications. This article breaks down how the gesture triggers at the system level, how it interacts with the Document Object Model (DOM), and the methods web developers use to adapt to or disable this feature.
The Native Trigger Mechanism
In iOS 15 Safari, pull-to-refresh activates when a user scrolls down while already at the top of a webpage.
- Scroll Position Check: The browser checks if the
root viewport's vertical scroll position (
window.scrollY) is at0. - Overscroll Detection: If the user performs a continuous downward swipe gesture starting from the top, Safari enters an elastic overscroll state, pulling the web page content down.
- Indicator Appearance: A native circular activity indicator appears above the top edge of the page.
- Reload Execution: If the user drags the page past a
predefined vertical threshold and releases, Safari issues a standard
page reload (
location.reload()). If the user releases before reaching the threshold, the page snaps back to the top without refreshing.
Interaction with Web Content
Safari distinguishes between the root document and nested scrollable containers.
- Root Document: Swiping down at the top of the
<body>or<html>element will trigger the native refresh mechanism. - Internal Scroll Areas: If a user scrolls inside an
element with
overflow-y: scrolloroverflow-y: auto, pull-to-refresh does not trigger as long as the scroll event remains isolated within that container. However, if the container reaches its top boundary and scroll chaining occurs, the gesture can bubble up to the viewport and initiate a refresh. - Fixed Elements: Elements styled with
position: fixedmove downward alongside the rest of the document during the overscroll bounce before snapping back.
Developer Control and Disabling the Feature
For web applications (such as Progressive Web Apps, canvas games, or sites with custom pull-to-refresh implementations), this native gesture can cause unintended page reloads.
CSS Prevention
In modern versions of Safari, the standard way to disable
pull-to-refresh is the CSS overscroll-behavior
property:
html, body {
overscroll-behavior-y: none;
}Setting overscroll-behavior-y: none prevents the
overscroll glow, bounce, and the associated refresh action while leaving
normal scrolling intact.
JavaScript Event Prevention
Because early iOS 15 builds had limited support for
overscroll-behavior, developers also relied on intercepting
touch events. By tracking the touchstart and
touchmove events, you can prevent the default browser
behavior if a downward swipe occurs at the top of the page:
let touchStartY = 0;
window.addEventListener('touchstart', (e) => {
touchStartY = e.touches[0].clientY;
}, { passive: false });
window.addEventListener('touchmove', (e) => {
const touchCurrentY = e.touches[0].clientY;
if (window.scrollY === 0 && touchCurrentY > touchStartY) {
e.preventDefault();
}
}, { passive: false });Setting { passive: false } is required, as Safari
defaults touch event listeners to passive, which disables the ability to
call e.preventDefault().