Skip to content

fix(sidebar): prevent docs page scrolling when typing in the sidebar filter#34

Merged
ChronosSF merged 4 commits into
masterfrom
aahmedov/fix-search-scroll-key-press
May 29, 2026
Merged

fix(sidebar): prevent docs page scrolling when typing in the sidebar filter#34
ChronosSF merged 4 commits into
masterfrom
aahmedov/fix-search-scroll-key-press

Conversation

@Zneeky
Copy link
Copy Markdown
Contributor

@Zneeky Zneeky commented May 13, 2026

Closes: #28
Closes: IgniteUI/igniteui-documentation#253

Summary

Fixes the sidebar filter input scrolling the docs page back toward the top on every keystroke (and on X-button click, Escape, held-Backspace, held-Delete) when the user has scrolled the page down.

Root cause (empirically confirmed)

Chrome runs an internal "scroll the focused editable into view" pass on every input mutation. Two facts conspire to make it visible in this layout:

  1. The filter <igc-input> lives inside a position: sticky sidebar. Visually it sits at the top of the viewport, but in the layout tree its document-flow position is wherever the sidebar would naturally be, i.e. above the viewport once the page is scrolled down.
  2. Chrome's smooth-scroll target computation uses the pre-sticky rect; its "already in view" early-exit uses the post-sticky visual rect. With scroll-behavior: smooth on <html> (set by DocsLayout's astro:page-load handler), the disagreement manifests as a smooth-animated scroll upward. With scroll-behavior: auto, the same code path still fires but as a single instant jump (~one row height per keystroke), which compounds across held keys.

This was verified by running document.documentElement.style.scrollBehavior = 'auto' in DevTools (animation gone, jumps still measurable as window.scrollY decreased by ~54px per keystroke).

What this is not

Ruled out via DevTools / instrumentation:

  • Not scroll-margin or overflow-anchor (CSS tweaks tried, bug persists).
  • Not any explicit JS call. scrollTo, scrollIntoView, and scrollTop setters were instrumented and produced no stack frames. The scroll is entirely browser-internal.
  • Not scroll anchoring (still reproduces with overflow-anchor: none).
  • Not Firefox (clean, Chromium-specific behavior).

How we handle it

A small, focused workaround in sidebar-filter.ts:

  1. pinScrollY() saves window.scrollY and snaps back every frame for ~200ms via requestAnimationFrame. rAF runs after layout but before paint, so the snap beats any visible scroll. Called at the top of onFilterInput, onFilterKeydown, and resetFilter. Held-key repeats extend the pin window in place (single loop, not parallel) so cascading Lit re-renders after exitFilterMode are also covered.
  2. While the pin window is active, pinScrollY also sets scroll-behavior: auto !important on <html> so the unwanted scroll is a single instant jump rather than a 250ms smooth animation that the pin would have to fight every frame (which caused visible stutter). The prior inline value is restored when the window ends, preserving DocsLayout's scroll-behavior: smooth for in-page anchor scrolling. !important is required because a third-party stylesheet sets scroll-behavior: smooth !important (confirmed via getComputedStyle).

Why not a cause-level fix

Empirically ruled out:

  • scroll-margin-top: -9999px on <igc-input> host and ::part(input). The caret-visibility pass ignores scroll-margin here.
  • overflow-anchor: none on <html> and *. Confirmed applied, bug persists.
  • Patching Element.prototype.scrollIntoView. No JS calls in the scroll stack.
  • Pure CSS rule (html:has(igc-input[data-sidebar-filter-input]:focus-within) { scroll-behavior: auto !important }). :focus-within on the host did not cross the igc-input shadow boundary reliably in our testing.
  • Restructuring the sidebar to position: fixed. Works but requires JS geometry sync against the grid column width and breaks on responsive breakpoints. Not worth the blast radius for a Chrome-only quirk.

Test plan

  • Scroll page down, type in the filter input. Page should not move.
  • Scroll page down, hold Backspace or Delete to clear input. Page should not move during or after.
  • Press Escape with a filter active. Page should not move.
  • Click the X clear button. Page should not move (this case previously stuttered).
  • Anchor-link navigation (clicking a heading anchor elsewhere on the page) still smooth-scrolls.
  • Firefox: no regression.

@Zneeky Zneeky changed the title fix: implement scroll locking during filter input and reset actions fix(sidebar): prevent docs page scrolling when typing in the sidebar filter May 29, 2026
@Zneeky Zneeky requested a review from ChronosSF May 29, 2026 14:09
@Zneeky Zneeky added the ❌ status: awaiting-test PRs awaiting manual verification label May 29, 2026
…scroll-into-view

Replace the 15-frame rAF scroll-lock with a leaner combo: disable smooth scroll
while the input has focus (so the unwanted scroll is a single instant jump,
not a 250ms animation) and pin scrollY for ~200ms after each keystroke,
extending the window on held-key repeats to cover Lit's batched re-renders.
@Zneeky Zneeky force-pushed the aahmedov/fix-search-scroll-key-press branch from 510e011 to f14b67f Compare May 29, 2026 14:13
@dobromirts dobromirts requested a review from Copilot May 29, 2026 14:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Workaround for a Chromium-only quirk where typing in the sidebar's igc-input filter (rendered inside a position: sticky sidebar) triggers a browser-internal "scroll focused editable into view" pass that visibly scrolls the docs page upward on each keystroke. The fix is scoped to sidebar-filter.ts and intentionally avoids restructuring layout.

Changes:

  • While the filter input has focus, force scroll-behavior: auto !important on <html> (overrides a third-party smooth !important), and clear it on blur.
  • New pinScrollY() helper captures window.scrollY and snaps it back via rAF for ~10 frames; held-key repeats extend the same loop instead of stacking parallel ones.
  • Call pinScrollY() at the top of onFilterInput, onFilterKeydown, and resetFilter to cover typing, Escape, Backspace/Delete repeat, and clear-button paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/DocsSidebar/sidebar-filter.ts Outdated
Previously the focus-time scroll-behavior toggle didn't cover the X clear
button (focus leaves the input before resetFilter runs, restoring smooth
mid-mutation and causing a visible stutter). Move the toggle into
pinScrollY itself so 'auto' is in effect for exactly the pin window,
regardless of focus state. Prior inline value (DocsLayout's 'smooth') is
restored when the window ends.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

@ChronosSF ChronosSF added 💥 status: in-test PRs currently being tested and removed ❌ status: awaiting-test PRs awaiting manual verification labels May 29, 2026
@ChronosSF ChronosSF self-assigned this May 29, 2026
@ChronosSF ChronosSF merged commit 59636c0 into master May 29, 2026
4 checks passed
@ChronosSF ChronosSF deleted the aahmedov/fix-search-scroll-key-press branch May 29, 2026 15:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💥 status: in-test PRs currently being tested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using the TOC Filter gradually scrolls the content area Typing in the DocsSidebar's search filter input scrolls the page

3 participants