From Chrome 129 you can use the scrollSnapChange
and scrollSnapChanging
events from JavaScript. By implementing built-in snap events, the previously invisible snap state will become actionable, at the right time, and always correct. This is not a convenience you had without these events.
Before scrollSnapChange
, you could use an intersection observer to find what element was crossing the scroll port, but determining what was snapped was restricted to a few circumstances. For example, you can detect if the snap items fill the scroll port or fill the majority of the scroll port. To do this you'd observe intersecting elements of the scroll area, then based on which item is consuming the majority of the scroll area, assume this was the snap target, then wait for scrollend
and act upon the item snapped (the snap target).
Before scrollSnapChanging
however, knowing when the snap target is changing or what it's being changed to (like given a scroll fling) was impossible.
A horizontal scroller is shown with numbered boxes inside as snap targets. On the left is a real time log of scrollSnapChange events, highlighting the snapTargetInline with blue. On the right is a real time log of scrollSnapChanging events, highlighting the snapTargetInline with gray.
Try it
https://codepen.io/web-dot-dev/pen/jOjaaEP
Great news, these new events make this information available quickly and easily. This unlocks scroll snap interactions to reach beyond their current capability and enables the orchestration of scroll snap relationships and fresh UI feedback scenarios.
Be considerate to screen readers and keyboard users when using scroll snap as a picker mechanism. The guidelines found in this Carousel Tutorial from the Web Accessibility Initiative will aid you in making accessible components.This event fires only if a scroll gesture has resulted in a new snap target having been rested on, and in the following order
scrollend
.This event fires just before scrollend
, when scrolling is complete, and only if a new snap target was rested upon. This makes the event feel lazy or just-in-time when the scroll gesture has completed.
scroller.addEventListener('scrollsnapchange', event => {
console.log(event.snapTargetBlock);
console.log(event.snapTargetInline);
})
scroller.onscrollsnapchange = event => {
console.log(event.snapTargetBlock);
console.log(event.snapTargetInline);
}
The event exposes the snapped item on the event object as snapTargetBlock
and snapTargetInline
. If the scroller is horizontal only, the snapTargetBlock
property will be null
. The value of the property will be the element node.
A finger still on the screen or fingers on a trackpad indicate the user's gesture is not finished scrolling, which means scroll has not ended, which means the snap target hasn't changed yet, it's pending a complete user gesture.
Does not fire if the snap target did not changeThe event is for snap change, and if the snap target did not change, the event won't fire, even if the scroller was interacted with by a user. The user did in fact scroll though, so upon scroll completion the scrollend
event still fires.
This event fires as soon as the browser has decided that the scroll gesture has or will result in a new snap target. It fires eagerly, and during scrolling.
scroller.addEventListener('scrollsnapchanging', event => {
console.log(event.snapTargetBlock);
console.log(event.snapTargetInline);
})
scroller.onscrollsnapchanging = event => {
console.log(event.snapTargetBlock);
console.log(event.snapTargetInline);
}
The event exposes the snapped item on the event object as snapTargetBlock
and snapTargetInline
. If the scroller is vertical only, the snapTargetInline
property will be null
. The value of the property will be the element node.
Unlike scrollSnapChange
which awaits a complete user scroll gesture, this event will fire eagerly while a user scrolls with their finger or using a trackpad. Consider a user who is scrolling slowly without lifting a finger, scrollSnapChanging
will fire multiple times during the gesture as long as the user is panning over multiple potential snap targets. Each time the user scrolls, if the browser has determined that upon release it would rest upon a new snap target, the event fires to tell you which element that is.
Furthermore, consider a fling, where a user does a scroll throw gesture that spans multiple snap targets at once; this event will fire once with the target that will be rested upon. So it's eager but not wasteful, supplying you with the snap target as soon as possible.
Use cases and examplesThese events enable many new use cases while also making current patterns much easier to implement. One prime example is enabling snap triggered animation. Contextually revealing the snap item, the snap item's children or associated information when it is the snap target.
The following patterns demonstrate some use cases to help you be productive straight away.
Highlight a testimonialThis example promotes or visually focuses the snapped testimonial.
scroller.onscrollsnapchange = event => {
scroller.querySelector(':scope .snapped')?.classList.remove('snapped')
event.snapTargetInline.classList.add('snapped')
}
https://codepen.io/web-dot-dev/pen/dyBZZPe Show the caption for the snapped item
This example shows the caption for the snapped item. Both events are included in this demo, so you can see the timing and user experience differences between scrollSnapChange
and scrollSnapChanging
.
Snap Changing
https://codepen.io/web-dot-dev/pen/wvLPPBL
Snap Change
https://codepen.io/web-dot-dev/pen/QWXOObw
This example knows when a new slide has been landed and rested on, which is a great time to animate the contents once.
document.addEventListener('scrollsnapchange', event => {
event.snapTargetBlock.classList.add('seen')
})
https://codepen.io/web-dot-dev/pen/rNEYYVj Snapping on both x and y in a scroller
Scroll snap works for scrollers that allow horizontal and vertical scrolling. This demo shows both scrollSnapChanging
and scrollSnapChange
targets as you scroll around the grid. This demo illuminates how the element the browser is snapping to may not always be the one you think it is.
A grid of squares in a horizontal and vertical scroller is shown. The dashed border represents the scrollSnapChanging target and the solid border is the scrollSnapChange target. Red represents snapTargetInline, and blue represents snapTargetBlock.
https://codepen.io/web-dot-dev/pen/qBzVVdp
Two linked scrollersThis demo has two scroll snapping containers where one is a high level list of links and the other is the actual paged content. The new scrollSnapChanging
event makes it trivial to link the snap states of these bi-directionally so they're always in sync.
This demo has 3 scrollers, each representing a different color channel in OKLCH. The snapped item is synchronized with its relevant radio group and the data can be retrieved from a form wrapping the inputs. To a mouse or touch user, you can scroll to the value you want. For keyboard users, you can tab and use the arrow keys. For a screen reader, it's just a form.
scrollSnapChanging is used to eagerly synchronize the snapped item with state, while scrollSnapChange is used to animate the affected color channel header that the user input was applied to.
https://codepen.io/web-dot-dev/pen/OJeOOVG Snapping staggering animated hubsThis demo progressively enhances the scroll snapping experience with snap triggered transitions using scrollsnapchange
.
Check for event support with the following JavaScript:
if ('onscrollsnapchange' in window) {
// ok to use snap change
}
https://codepen.io/web-dot-dev/pen/MWMOOae Scrollable ruler input
This demo features a scrollable ruler as an alternative way to pick a length for a number input. Enter values directly into the number input or scroll to the size. The changing event is used to clear the selection during the user's gesture, while the change event is used to update state and reinforce the user's choice.
https://codepen.io/web-dot-dev/pen/LYKOOpd Cover flowThis demo builds upon Bramus Van Damme's excellent scroll driven animation recreation of the famous macOS cover flow (video tutorial too). Uniquely, scrollSnapChanging
is used to hide the album title and scrollSnapChange
is used to present the title. The events help orchestrate an eager hide of the old title and a lazy presentation of the new one.
Now that it's trivial to know which element is about to be snapped and which is actively snapped, there are many new possibilities! Here's a list of ideas to help inspire creativity and additional use cases:
The Chrome team is excited to hear what you build with these new APIs and hope it helps streamline your scrollable experiences.
Resources:RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4