Skip to content

React + epub.js scrolled-doc: upward scroll snaps back (scroll anchoring issue) — workaround included #1416

@SungWookkk

Description

@SungWookkk

Hi,

I’m combining my previous symptom report and workaround notes into one issue.

Related: #1303 (comment)

Summary

In scrolled-doc mode (React app), fast upward scrolling causes the viewport to jump back down repeatedly, as if the reader is “pulled” to a previous position.
This is especially visible while sections are loading/injected in continuous scroll mode.

It appears to be caused by browser scroll anchoring interacting with epub.js section rendering.

Environment

  • epub.js (futurepress/epub.js) in React
  • Flow: scrolled-doc
  • Manager: continuous
  • Browser: Chromium-based (also reproducible in other modern browsers)
  • Large EPUB with many sections

Reproduction

  1. Open a large EPUB in scrolled-doc mode.
  2. Scroll down, then scroll up quickly multiple times.
  3. Observe that scroll position intermittently snaps back downward.

Expected

Scrolling should be stable and monotonic (up stays up, down stays down).

Actual

When scrolling up quickly, position jumps back down (rubber-band/snap-back behavior).

Confirmed workaround

Disabling scroll anchoring fixes the issue in my app.

Basic CSS workaround

.epub-container {
  overflow-anchor: none !important;
}

React-side workaround (container + iframe content docs)

useEffect(() => {
  if (!rendition) return;

  // Stage/manager container
  const container = (rendition as any)?.manager?.container as HTMLElement | undefined;
  if (container) container.style.overflowAnchor = "none";

  // Each loaded section document (iframe content)
  (rendition as any).hooks.content.register((contents: any) => {
    const doc = contents?.document as Document | undefined;
    if (!doc) return;

    doc.documentElement.style.setProperty("overflow-anchor", "none", "important");
    doc.body.style.setProperty("overflow-anchor", "none", "important");

    doc.querySelectorAll<HTMLElement>(".epub-container").forEach((el) => {
      el.style.setProperty("overflow-anchor", "none", "important");
    });
  });

  return () => {
    if (container) container.style.removeProperty("overflow-anchor");
  };
}, [rendition]);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions