jQuery Scroll Event

Beginner
⏱️ 10 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
Browser events

What You’ll Learn

The scroll event fires when scroll position changes inside an element. This tutorial covers .on("scroll") on overflow containers and $(window), logging to #log, .trigger("scroll"), page-scroll indicators, reading scrollTop, throttling fast handlers, the official jQuery API demos, and how this differs from resize, .scrollTop(), and the deprecated .scroll() method.

01

.on()

Bind handler

02

#target

Overflow box

03

$(window)

Page scroll

04

Throttle

Limit work

05

.trigger()

Fire scroll

06

Since 1.7

Modern API

Introduction

Sticky headers, infinite scroll feeds, and reading progress bars all react when the user moves through content. The scroll event is jQuery’s hook for that moment — when scroll position changes inside a scrollable element, whether that element is the browser window or a div with overflowing content.

The official jQuery API distinguishes the scroll event (bound with .on("scroll") since 1.7) from the deprecated .scroll() method (legacy shorthand, deprecated since 3.3). To programmatically run scroll handlers, use .trigger("scroll") since jQuery 1.0.

Understanding the scroll Event

The scroll event is sent to an element when the user scrolls to a different place in the element. jQuery wraps the native event so you can bind with $("#target").on("scroll", handler) or $(window).on("scroll", handler) and chain further calls on the same collection.

It applies to window objects, scrollable frames, and elements with the overflow CSS property set to scroll or auto when the element’s explicit height or width is less than its contents. Scroll fires whenever scroll position changes — mouse wheel, scrollbar drag, arrow keys, touch, or programmatic .scrollTop() changes.

💡
Beginner Tip

Bind on the element that actually scrolls. Use $(window) for page scroll and $("#panel") for an overflow container — binding on a parent that does not scroll will never fire.

📝 Syntax

The modern jQuery API for the scroll event has two main forms — binding a handler and triggering the event:

1. Bind a handler — .on("scroll" [, eventData ], handler) (since 1.7)

jQuery
.on( "scroll" [, eventData ], handler )
  • eventData — optional object passed to the handler as event.data.
  • handler — function called when scroll position changes: function( event ) { ... }.
  • Bind on the scrollable element — window, a frame, or an overflow container whose content exceeds its box.

2. Trigger the event — .trigger("scroll") (since 1.0)

jQuery
.trigger( "scroll" )
  • Runs bound scroll handlers on the matched element(s) — typically $("#target") or $(window).
  • Useful when your code changes scroll position and downstream widgets expect a scroll notification.

3. Unbind — .off("scroll" [, handler])

jQuery
$( "#target" ).off( "scroll" );              // remove all scroll handlers
$( "#target" ).off( "scroll", throttledFn ); // remove one handler

Official jQuery API examples

jQuery
$( "#target" ).on( "scroll", function() {
  $( "#log" ).append( " Handler for `scroll` called. " );
} );

$( "#other" ).on( "click", function() {
  $( "#target" ).trigger( "scroll" );
} );

$( window ).on( "scroll", function() {
  $( "span" ).css( "display", "inline" ).fadeOut( "slow" );
} );

Deprecated shorthand

jQuery also provided .scroll(handler) as a legacy binding shorthand and no-arg .scroll() to trigger the event. The binding form is deprecated since jQuery 3.3 — see the jQuery .scroll() method tutorial (coming soon) or migrate to .on("scroll") and .trigger("scroll").

Return value

  • .on() and .trigger() return the original jQuery object for chaining.
  • Inside the handler, this refers to the DOM element that scrolled — use $(this).scrollTop() or $(window).scrollTop() for the current offset.

⚡ Quick Reference

GoalCode
Bind scroll on overflow element$("#target").on("scroll", fn)
Bind scroll on page (window)$(window).on("scroll", fn)
Append scroll log lines$("#log").append("Handler called. ")
Read vertical scroll offset$(window).scrollTop() inside handler
Trigger scroll programmatically$("#target").trigger("scroll")
Throttle expensive scroll workif (Date.now() - last >= 150) { ... }
Remove scroll handlers$(window).off("scroll")

📋 scroll vs resize vs .scrollTop() vs deprecated .scroll()

Four related concepts that sound similar — know which reacts to scroll position, viewport size, programmatic offset, and legacy jQuery shorthand.

scroll event
element

Scroll position changed inside a scrollable element — bind with .on("scroll") on window or overflow containers; fires on wheel, scrollbar, keys, and drag

resize event
window

Browser viewport size changed — bind with .on("resize") on $(window); not the same as content scrolling inside the page

.scrollTop() method
get/set

Get or set vertical scroll offset in pixels — use to jump to top or read position; pair with scroll event handlers for reactive UI

.scroll() method
deprecated

Legacy bind (.scroll(fn)) or trigger (.scroll()) — deprecated since 3.3; use .on("scroll") and .trigger("scroll") in new code

Examples Gallery

Examples 1–3 follow the official jQuery API documentation. Examples 4–5 extend the pattern with a scrollTop progress bar and throttled container scroll. Use the Try-it links to run each snippet in the browser.

📚 Official API Demos

Official jQuery demos for logging scroll on #target, triggering scroll, and reacting to page scroll.

Example 1 — Official Demo: Append to #log on #target Scroll

Log each scroll call when the user scrolls inside a scrollable div — from the jQuery API documentation.

jQuery
$( "#target" ).on( "scroll", function() {
  $( "#log" ).append( " Handler for `scroll` called. " );
} );
Try It Yourself

How It Works

Binding on #target catches every scroll position change inside that overflow container. Appending to #log makes the firing pattern visible — scroll the Try-it panel to see how often the event fires.

Example 2 — Official Demo: #other Triggers Scroll on #target

Fire scroll handlers programmatically with a button click — official jQuery API pattern.

jQuery
$( "#target" ).on( "scroll", function() {
  $( "#log" ).text( "Handler for scroll called." );
} );

$( "#other" ).on( "click", function() {
  $( "#target" ).trigger( "scroll" );
} );
Try It Yourself

How It Works

.trigger("scroll") synthetically fires the event on #target. Bound handlers run even though the user did not scroll — ideal for testing or syncing UI after programmatic .scrollTop() changes.

Example 3 — Official Demo: $(window).on("scroll") Page Indicator

Show a message when the page is scrolled — official jQuery API window scroll pattern.

jQuery
$( window ).on( "scroll", function() {
  $( "#msg" ).css( "display", "inline-block" ).stop( true, true ).fadeOut( 800 );
} );
Try It Yourself

How It Works

$(window).on("scroll") listens for page scroll. The official API uses a span that fades out; this tutorial’s Try-it uses #msg with the same show-and-fade pattern via .stop(true, true).fadeOut().

📈 Production Patterns

Read scrollTop for progress UI and throttle handlers on fast-scrolling containers.

Example 4 — scrollTop Reading Progress Bar

Read $(window).scrollTop() inside the scroll handler to drive a fixed progress bar.

jQuery
function updateProgress() {
  var st = $( window ).scrollTop();
  var max = $( document ).height() - $( window ).height();
  var pct = max > 0 ? Math.round( ( st / max ) * 100 ) : 0;
  $( "#bar" ).css( "width", pct + "%" );
  $( "#pct" ).text( pct + "%" );
}

$( window ).on( "scroll", updateProgress );
updateProgress();
Try It Yourself

How It Works

The scroll event tells you when position changed; .scrollTop() tells you where. Dividing current offset by the maximum scrollable distance yields a percentage for progress bars, lazy-load thresholds, and section spy navigation.

Example 5 — Throttled Scroll Handler on #panel

Limit how often expensive scroll work runs — handlers can fire very frequently during fast scrolling.

jQuery
var raw = 0, th = 0, last = 0;

$( "#panel" ).on( "scroll", function() {
  raw++;
  $( "#raw" ).text( raw );

  var now = Date.now();
  if ( now - last >= 150 ) {
    last = now;
    th++;
    $( "#th" ).text( th );
  }
} );
Try It Yourself

How It Works

A timestamp gate collapses dozens of scroll events into periodic updates. This is the standard pattern for parallax, sticky sidebars, and any DOM measurement that would stutter if run on every intermediate pixel during a fast flick scroll.

🚀 Common Use Cases

  • Sticky headers and nav — toggle fixed positioning or shadow classes when $(window).scrollTop() passes a threshold.
  • Reading progress bars — update a top bar or sidebar indicator as the user moves through long articles.
  • Infinite scroll feeds — load the next page when scroll nears the bottom of a container or the document.
  • Scroll-spy navigation — highlight the active section link based on which heading is currently in view.
  • Overflow panel sync — bind on chat logs, data tables, or map sidebars that scroll independently of the page.
  • Manual scroll notifications — use .trigger("scroll") after programmatic .scrollTop() changes so widgets that only listen for scroll also update.

🚀 How the scroll Event Flows

1

Handler bound on scrollable element

Code calls $("#target").on("scroll", fn) or $(window).on("scroll", fn) — optionally with eventData for config objects.

bind
2

Scroll position changes

The user wheels, drags the scrollbar, presses arrow keys, or touches a trackpad. The browser sends scroll to the element whose offset changed — window or overflow container.

native
3

Handler runs (maybe many times)

jQuery invokes your callback. Read scrollTop, update progress UI, or queue throttled work — scroll can fire dozens of times per second during fast movement.

handler
4

UI reflects scroll position

Indicators, lazy loads, and sticky nav stay in sync. For programmatic changes, call .trigger("scroll") so widgets that only listen for scroll also update.

📝 Notes

  • Bind with .on("scroll", handler) since jQuery 1.7 — not the deprecated .scroll() shorthand.
  • Trigger with .trigger("scroll") since jQuery 1.0.
  • Applies to window, scrollable frames, and elements with overflow:scroll or overflow:auto when content overflows.
  • Scroll fires whenever scroll position changes — wheel, scrollbar, keys, drag, or touch — regardless of the cause.
  • Throttle or debounce expensive layout, measurement, and animation code inside scroll handlers.
  • scroll is not resize — resize fires when viewport size changes, not when content position moves.
  • The scroll event is not .scrollTop() — that method gets or sets offset; the event notifies you when offset changes.
  • Use .off("scroll") to remove handlers — avoid deprecated .unbind("scroll") or legacy .scroll().

Browser Support

The scroll event is a standard DOM event on scrollable elements and window, supported in every browser jQuery targets. jQuery’s .on("scroll") (since 1.7) and .trigger("scroll") (since 1.0) provide consistent binding and chaining. Scroll can fire very frequently — throttle production handlers.

jQuery 1.7+

jQuery scroll event

Supported in jQuery 1.x, 2.x, and 3.x across all modern browsers and IE with supported jQuery versions. Native equivalent: element.addEventListener('scroll', fn) — jQuery adds collection binding, eventData, and .trigger() in one chainable API.

100% With jQuery loaded
Google Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All modern versions
Full support
scroll Universal

Bottom line: Safe in any jQuery project. Prefer .on('scroll') over the deprecated .scroll() method. Bind on the element that scrolls and throttle heavy work.

Conclusion

The jQuery scroll event is the standard hook for scroll position changes. Bind handlers with .on("scroll", fn) on overflow elements or $(window), read position with .scrollTop(), throttle expensive work, and fire handlers programmatically with .trigger("scroll").

Remember: scroll fires on the element that moves, not on parents that stay fixed; this is not the resize event; and the legacy .scroll() shorthand is deprecated since jQuery 3.3. Pair scroll handlers with .scrollTop() when you need the actual offset.

💡 Best Practices

✅ Do

  • Bind with .on("scroll", handler) on the element that actually scrolls
  • Use $(window) for page scroll and $("#panel") for overflow containers
  • Throttle or debounce layout, parallax, and DOM measurements in scroll handlers
  • Read .scrollTop() inside handlers when you need the current offset
  • Call .trigger("scroll") after programmatic scroll position changes
  • Remove handlers with .off("scroll") when widgets unmount

❌ Don’t

  • Confuse with deprecated .scroll() — migrate to .on("scroll")
  • Bind on a parent that does not scroll expecting child scroll events to bubble
  • Run heavy synchronous work on every scroll fire without throttling
  • Confuse scroll with resize or with .scrollTop() get/set
  • Assume scroll fires once per user gesture — count varies with scroll speed

Key Takeaways

Knowledge Unlocked

Six things to remember about the scroll event

Element, window, throttle.

6
Core concepts
# 02

#target

Overflow

Element
win 03

$(window)

Page

Target
04

Throttle

No jank

Perf
05

.trigger()

Manual

Programmatic
06

Not resize

Position

Compare

❓ Frequently Asked Questions

The scroll event is sent to an element when the user scrolls to a different place in that element. Bind with .on('scroll', handler) since jQuery 1.7. It applies to window, scrollable frames, and elements with overflow:scroll or overflow:auto when content overflows. Scroll fires whenever scroll position changes — wheel, scrollbar drag, arrow keys, or touch — regardless of the cause.
Use $(window).on('scroll', fn) for page-level scroll — back-to-top buttons, sticky headers, and reading progress bars. Bind on a specific element like $('#panel').on('scroll', fn) when only that container scrolls, such as a chat log or data table body. The event target must be the element whose scrollTop or scrollLeft actually changes.
Call $('#target').trigger('scroll') since jQuery 1.0. Bound scroll handlers on that element run as if the user scrolled — useful for testing or syncing UI when you change scroll position with .scrollTop(). Example 2 on this page uses a button to fire trigger('scroll') on #target.
Throttle for visual updates that should stay responsive while scrolling — progress bars, parallax, and sticky nav checks often run every 100–150 ms during scroll. Debounce when you only need the final position after scrolling stops — lazy-loading images near the viewport bottom. Scroll can fire dozens of times per second; never run heavy DOM work on every raw event.
Scroll fires when content position changes inside a scrollable element — the viewport size stays the same. Resize fires on window when the browser viewport width or height changes. A user can scroll without resizing and resize without scrolling. Bind scroll for reading position; bind resize for layout reflow.
They target the same browser scroll event — .scroll(handler) is the legacy shorthand deprecated since jQuery 3.3, while .on('scroll', handler) is the modern replacement since 1.7. Unlike .error(), which was removed in jQuery 3.0, .scroll() still works in jQuery 3.x but should not be used in new code. Do not confuse the scroll event with the .scrollTop() method, which gets or sets scroll position rather than listening for changes.
Did you know?

The jQuery API page for scroll notes that a scroll event is sent whenever the element’s scroll position changes, regardless of the cause — a mouse click or drag on the scroll bar, dragging inside the element, pressing arrow keys, or using the mouse wheel could all trigger it. That is why counting scroll calls is unreliable during fast movement — production code throttles layout work instead. The same page documents that the event applies to window and to elements with overflow:scroll or overflow:auto, which is why you bind on the specific scrollable element rather than expecting all scroll activity to bubble from children.

Next: jQuery .scroll() Method

Legacy shorthand deprecated since 3.3 — learn migration to .on("scroll").

.scroll() method tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful