Scrolling
Scrolling is one of the few things a user does constantly without thinking about it. Every flick of a trackpad, every drag of a scrollbar, every arrow-key press moves the viewport. The scroll event is how your code finds out.
Once you can observe the scroll position, a lot of familiar UI patterns open up:
- Reveal or hide a sticky header, a “back to top” button, or a floating toolbar depending on how far down the page someone is.
- Fetch and append the next batch of items when the reader nears the bottom — the “infinite scroll” pattern you see on feeds and search results.
- Drive progress indicators, parallax effects, or scroll-linked animations.
Here’s the smallest useful example. It reads the current vertical scroll offset and writes it into an element on every scroll:
window.addEventListener('scroll', function() {
document.getElementById('scrollDepth').innerHTML = window.pageYOffset + 'px';
});
In action:
Current scroll = scroll the window
The scroll event fires on the window for the whole page, and it also fires on any individual element that has its own scrollbar (an element with overflow: scroll or overflow: auto whose content is taller or wider than its box).
Because a live page can be awkward to scroll inside a small preview, the demo below gives an element its own scrollbar and reports its scrollTop — the element-level counterpart to window.scrollY. Scroll inside the box and watch the readout track your position:
A scroll offset only becomes useful once you turn it into something visible. Scroll progress bars — the thin line that fills as you move down an article — are the classic case. Convert the current offset into a percentage of the total scrollable distance and paint it:
window.scrollY reads 120Why scroll handlers must stay cheap
The scroll event can fire a lot — many times per second during a fast flick. Whatever you put in the handler runs on that hot path, so heavy work there makes scrolling feel sluggish.
Two habits keep things smooth:
- Read, don’t thrash. Reading
scrollYis cheap. Repeatedly writing to the DOM and forcing layout recalculation inside the handler is not. - Throttle or debounce when the reaction doesn’t need to happen on every single tick — for example, “load more when near the bottom” only needs to check occasionally.
Prevent scrolling
Say you want to make something unscrollable — maybe a modal is open and the page behind it should stay put. How?
The instinct is to call event.preventDefault() inside the scroll handler. That doesn’t work. The scroll event is a report, not a request: it fires after the browser has already moved the viewport. Cancelling it is like hitting the brakes after you’ve already stopped — there’s nothing left to prevent.
wheel / key / drag
(already happened)
preventDefault = no-op
The trick is to cancel the event that causes the scroll, before the browser acts on it. Those source events fire earlier in the chain and are cancelable. For example, the keydown event for PageUp and PageDown:
// Block scrolling triggered by the PageUp / PageDown keys.
window.addEventListener('keydown', function(event) {
// 33 = PageUp, 34 = PageDown
if (event.key === 'PageUp' || event.key === 'PageDown') {
event.preventDefault();
}
});
Add a handler like this to the causing event and call event.preventDefault() inside it, and the scroll never starts.
Because of that, the reliable way to make something unscrollable is CSS, not JavaScript. The overflow property controls whether an element scrolls at all:
/* Freeze the whole page — a common trick while a modal is open. */
body {
overflow: hidden;
}
With overflow: hidden, the element simply has no scroll mechanism, so no input can move it. There’s no event to cancel because there’s no scroll to begin with.
Try it: the button toggles the box between overflow: auto and overflow: hidden. When it’s locked, no amount of wheeling, dragging, or arrow-keying will budge the content — exactly the behaviour you want behind an open modal.
keydown, wheel…
viewport moves
too late to cancel
The tasks that follow put onscroll to work — showing a “back to top” button, building endless page loading, and similar patterns. Read through them to see the event applied.