ScreenshotNeo

BlogGuides

Why Scrolling Fails on Some Websites and How to Fix It

Find out why a website will not scroll, isolate the cause quickly, and fix browser, input, layout, event-handler, and nested-panel problems.

By the ScreenshotNeo team1 October 20268 min read

Short answer: first determine whether scrolling fails on one website, in one browser, or with one input method. That split tells you whether to inspect the page, browser state, or hardware/settings. A page can block scrolling with JavaScript, route the gesture to a nested panel or iframe, leave overflow: hidden after a modal closes, or become sluggish while the main thread is busy.

This guide covers the reader checks that isolate the problem and the page-level fixes developers can verify in code.

1. Identify which layer is failing

What changes? Most useful next check
Only one website Inspect overlays, scroll containers, event handlers, and site data.
Every website in one browser Disable extensions, check blocked scripts/settings, clear site data, and compare another browser.
Only mouse wheel or touchpad Try keyboard scrolling, scrollbar dragging, and another pointing device.
Only touch Inspect touch-action, touch listeners, overlays, and the actual touch target.
Only a modal, drawer, chat panel, or iframe Scroll inside that element and inspect nested scroll boundaries.

2. Reader troubleshooting checklist

  1. Reload once. Look for a loading screen, consent layer, menu, dialog, or invisible overlay covering the page.
  2. Try Page Down, the spacebar, arrow keys, scrollbar dragging, mouse wheel, and touchpad. If one method works, the page may be fine and the input path is the problem.
  3. Close open menus, drawers, dialogs, and embedded panels. Try scrolling both inside the panel and on the page outside it.
  4. Open the same URL in another browser. Mozilla recommends this comparison because it shows whether the problem is specific to Firefox or more likely belongs to the website: Mozilla’s troubleshooting guide.
  5. Disable extensions one at a time, starting with ad blockers, privacy tools, script blockers, and page-customization extensions. Reload after each change.
  6. Clear cookies and site data for the affected domain if the faster checks fail. This can sign you out, so use it after the comparison tests.
  7. If the page fails across browsers and input methods, report the URL and exact reproduction steps to its owner.

3. Check for an overlay or a scroll lock

An element can cover the page while appearing transparent. In DevTools, use the element picker and move over the area where scrolling fails. Inspect fixed and absolute elements with a high z-index, full-viewport dimensions, or pointer-events rules.

For modal interfaces, verify that the lock is removed on every close path, including Escape, browser Back, route changes, and failed network requests.

function lockPage() {
  document.documentElement.classList.add('is-locked');
}

function unlockPage() {
  document.documentElement.classList.remove('is-locked');
}

// Keep the lock narrow and reversible.
.is-locked {
  overflow: hidden;
}

Inspect html, body, and major app wrappers for constrained heights, overflow: hidden, or an overflow: auto container that is only a few pixels tall. Confirm which element’s scrollHeight exceeds its clientHeight:

for (const el of [document.documentElement, document.body, ...document.querySelectorAll('*')]) {
  const style = getComputedStyle(el);
  if (el.scrollHeight > el.clientHeight && /(auto|scroll)/.test(style.overflowY)) {
    console.log('Scrollable:', el, {
      scrollHeight: el.scrollHeight,
      clientHeight: el.clientHeight,
      overflowY: style.overflowY
    });
  }
}

4. Fix JavaScript that cancels scrolling

A touchstart or first touchmove handler that calls preventDefault() cancels the browser’s normal touch panning. Use cancellation only for a component that truly owns the gesture. Chrome’s touch guidance recommends expressing the intended gesture with touch-action where possible: Making touch scrolling fast by default.

For observation-only handlers, mark the listener passive:

window.addEventListener('touchmove', (event) => {
  analytics.record('touch-move');
}, { passive: true });

window.addEventListener('wheel', (event) => {
  updateScrollIndicator(event.deltaY);
}, { passive: true });

Do not apply touch-action: none to the whole page: it disables browser scrolling and zooming within that element. Apply the smallest rule that matches the component:

/* Horizontal carousel: preserve vertical page panning and pinch zoom. */
.carousel {
  touch-action: pan-y pinch-zoom;
}

/* A canvas that intentionally owns every gesture. */
.map-canvas {
  touch-action: none;
}

For a custom gesture that must cancel scrolling, keep the non-passive listener scoped and short:

const canvas = document.querySelector('.map-canvas');
canvas.addEventListener('touchmove', (event) => {
  event.preventDefault();
  moveMap(event.touches[0]);
}, { passive: false });

Wheel handlers can also delay scrolling while the browser waits to see whether they will cancel the event. Chrome’s 2019 article reports that 75% of root-target wheel listeners lacked a passive option, more than 98% did not call preventDefault(), and fewer than 0.3% of pages were expected to risk unintended behavior from that intervention. These are historical Chrome measurements, not current prevalence estimates: Making wheel scrolling fast by default.

5. Diagnose nested panels, modals, and iframes

Chat panels, drawers, code editors, and iframes can each have their own scroll position. A gesture may be delivered to the inner element, stop at its boundary, or transfer to the parent. Firefox documents this as scroll handoff in its asynchronous panning architecture: Firefox Source Docs.

Record the target under the pointer or touch point and inspect its ancestors:

document.addEventListener('wheel', (event) => {
  console.log('wheel target:', event.target);
  console.log('path:', event.composedPath());
}, { passive: true });

Use overscroll-behavior when the boundary behavior is intentional, such as preventing a modal’s scroll from moving the document behind it. It controls scroll chaining; it does not repair a missing or locked main scroller. See Chrome’s scroll chaining guidance.

.dialog__content {
  overflow: auto;
  overscroll-behavior: contain;
}

For an iframe, test whether the frame itself can scroll and whether its document has enough content. Cross-origin frames cannot be inspected directly; ask the frame owner to check its own overflow and event handlers.

6. Separate failure from scroll jank

If the page eventually moves but feels delayed, profile the main thread. Long JavaScript tasks, forced layout, expensive scroll handlers, and third-party scripts can make input feel frozen. The input methods are not interchangeable: keyboard, wheel, touchpad, and touch can take different browser paths.

  • Keep scroll and pointer handlers small.
  • Read layout once, batch writes, and use requestAnimationFrame for visual updates.
  • Use passive listeners when cancellation is unnecessary.
  • Remove work from the critical path of the gesture; defer analytics and nonvisual work.
  • Test on the affected device, not only desktop emulation.

7. Browser and site-state causes

Cached data, extensions, blocked scripts or content, tracking protection, browser settings, and graphics configuration can make one page malfunction. Mozilla’s current support steps cover clearing site data, disabling extensions individually, comparing browsers, and checking settings: Websites look wrong or appear differently.

Change one variable at a time so you know which action fixed the problem. If private browsing works, compare extensions and site storage before changing hardware acceleration or advanced preferences.

8. A developer’s repeatable diagnostic procedure

  1. Write down scope: one site or many, one browser or many, one device or many.
  2. Write down input: wheel, touchpad, touch, keyboard, scrollbar, or a combination.
  3. Classify the symptom: total failure, delay/jank, inner panel only, or unexpected background movement.
  4. Use DevTools to identify the element that actually scrolls and inspect overflow, height, and scroll dimensions.
  5. Search application and third-party bundles for wheel, mousewheel, touchstart, touchmove, pointer handlers, and preventDefault().
  6. Check overlays and modal cleanup paths.
  7. Test the smallest CSS or JavaScript change on every affected browser and device combination.

9. Common errors and fixes

Symptom or console message Likely cause Fix
“Unable to preventDefault inside passive event listener” The listener is passive but tries to cancel scrolling. Remove cancellation, use touch-action, or deliberately register a narrowly scoped {passive:false} listener.
Keyboard scroll works; wheel does not Wheel handler, extension, or device setting. Disable extensions, inspect wheel listeners, and compare another device.
Only a modal scrolls Focus and gesture are captured by the modal or its inner scroller. Scroll the modal content; verify its overflow and boundary rules.
Background moves behind a dialog Scroll chaining or incomplete page lock. Lock the document while open and use overscroll-behavior on the dialog content.
Page is frozen after closing a menu Stale overflow:hidden or an invisible overlay. Trace every close path and remove the class/overlay in cleanup.
Touch fails but mouse works Touch listener cancellation, touch-action, or a touch-only overlay. Inspect touch targets and handlers on a real device.

10. Performance, reliability, and testing notes

  • Use a test matrix that records browser, operating system, device, input method, viewport, open overlays, and extension state.
  • Test at the top, middle, and bottom of every nested scroller. Boundary behavior often differs from middle-of-content behavior.
  • Include slow-network and partially loaded states. A loading overlay or script timeout can look like a scroll lock.
  • Keep fixes local to the component. Global event cancellation and global touch-action rules create regressions elsewhere.
  • Retest zoom, keyboard access, reduced-motion settings, screen readers, and pinch zoom after changing gesture behavior.

11. Or skip the browser setup

If you need a reliable image of a page while diagnosing or documenting a scrolling issue, ScreenshotNeo provides a single screenshot API request. Cookie banners, newsletter popups, and chat widgets are removed before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Its MCP server lets Claude, Cursor, and other MCP clients use take_screenshot, get_page_info, and capture_pdf.

See the ScreenshotNeo API docs for all options.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -o shot.webp

python - <<'PY'
import requests
r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
PY

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const image = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.webp', image);

Each response identifies the page verdict and billing status with X-Page-Verdict and X-Billed headers. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

12. FAQ

Why can I scroll with the keyboard but not the mouse?

Keyboard scrolling bypasses wheel handlers and some pointer-device paths. Check extensions, wheel listeners, and device settings before changing page CSS.

Can overscroll-behavior make a broken page scroll?

No. It changes what happens at a scroll boundary. It cannot create a missing scroller or remove an accidental scroll lock.

Should I make every event listener passive?

No. Make observation-only wheel and touch listeners passive. A component that intentionally cancels a gesture needs a narrowly scoped non-passive listener.

Why does the issue disappear in another browser?

The browser may differ in extension state, blocked content, site data, event defaults, graphics settings, or implementation details. Compare those layers before blaming hardware.

What information should I send a site owner?

Send the URL, browser and version, device, input method, exact symptom, whether another browser works, whether extensions are disabled, and whether the failure occurs in a nested panel or iframe.