Fix long sidebar jumping issue with custom viewports
Problem
Long sidebars were jumping when using a custom scrollable element as the viewport instead of the default window viewport. This issue occurred specifically when scrolling down and the sidebar transitioned to bottom-fixed positioning.
The problem was demonstrated in the provided JSFiddle examples:
- ✅ Default viewport (window/document): worked correctly
- ❌ Custom viewport (scrollable element): sidebar jumped during transitions
Root Cause
The issue was in the computeElementDimensions function in src/dimension-observer.js. The function calculated element positions by adding viewportTop to getBoundingClientRect() values, but this approach had a coordinate system mismatch:
-
getBoundingClientRect()always returns positions relative to the browser window - For window viewport:
viewportTopispageYOffset(document scroll) ✅ - For custom viewport:
viewportTopiselement.scrollTop(internal scroll) ❌
This mismatch caused incorrect position calculations, leading to the jumping behavior.
Solution
Modified computeElementDimensions to handle custom viewports correctly:
Window viewport (unchanged):
return {
top: rect.top + viewportTop, // rect.top + pageYOffset
bottom: rect.bottom + viewportTop,
height: rect.height
}
Custom viewport (fixed):
return {
top: rect.top + documentScrollTop, // rect.top + document scroll
bottom: rect.bottom + documentScrollTop,
height: rect.height
}
Changes
- Updated
computeElementDimensionsto accept$viewportparameter and differentiate between window and custom element viewports - Updated all function calls to pass the viewport parameter
- Added comprehensive tests in
test/custom-viewport.test.jsto validate the fix and prevent regressions
Testing
The fix has been validated with:
- New tests that reproduce the jumping issue and verify it's resolved
- Existing tests continue to pass (no regressions for window viewport usage)
- Tests cover various scroll positions and sidebar state transitions
This resolves the coordinate system mismatch that caused the jumping behavior described in the issue.
[!WARNING]
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
accounts.google.com
- Triggering command:
/opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --disable-dev-shm-usage --use-angle=swiftshader-webgl --mute-audio --crashpad-handler-pid=3504 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/puppeteer_dev_chrome_profile-MJK2ap --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,1838967325958793765,12419158326437385471,262144 --enable-features=PdfOopif --disable-features=AcceptCHFrame,IsolateSandboxedIframes,MediaRouter,OptimizationHints,PaintHolding,ProcessPerSiteUpToMainFrameThreshold,RenderDocument,Translate --variations-seed-version(dns block)- Triggering command:
/opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-REDACTED-timer-throttling --disable-REDACTEDing-occluded-windows --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-REDACTED-pages --disable-crash-reporter --disable-default-apps --disable-dev-shm-usage --disable-hang-monitor --disable-infobars --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-REDACTEDing --disable-search-engine-choice-screen --disable-sync --enable-automation --export-tagged-pdf --force-color-profile=srgb --generate-pdf-document-outline --metrics-recording-only --no-first-run --password-store=basic --use-mock-keychain --disable-features=Translate,AcceptCHFrame,MediaRouter,OptimizationHints,RenderDocument,ProcessPerSiteUpToMainFrameThreshold,IsolateSandboxedIframes --enable-features=PdfOopif --headless=new --hide-scrollbars --mute-audio --disable-extensions --remote-debugging-port=0 --user-data-dir=/tmp/puppeteer_dev_chrome_profile-MJK2ap --noerrdialogs --ozone-platform=headless --ozone-override-screen-size=800,600 --use-angle=swiftshader-webgl about:blank(dns block)- Triggering command:
/opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --disable-dev-shm-usage --use-angle=swiftshader-webgl --mute-audio --crashpad-handler-pid=4083 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/puppeteer_dev_chrome_profile-f9li5D --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,9800445507747328728,9185029744226650521,262144 --enable-features=PdfOopif --disable-features=AcceptCHFrame,IsolateSandboxedIframes,MediaRouter,OptimizationHints,PaintHolding,ProcessPerSiteUpToMainFrameThreshold,RenderDocument,Translate --variations-seed-version(dns block)android.clients.google.com
- Triggering command:
/opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --disable-dev-shm-usage --use-angle=swiftshader-webgl --mute-audio --crashpad-handler-pid=4083 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/puppeteer_dev_chrome_profile-f9li5D --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,9800445507747328728,9185029744226650521,262144 --enable-features=PdfOopif --disable-features=AcceptCHFrame,IsolateSandboxedIframes,MediaRouter,OptimizationHints,PaintHolding,ProcessPerSiteUpToMainFrameThreshold,RenderDocument,Translate --variations-seed-version(dns block)- Triggering command:
/opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-REDACTED-timer-throttling --disable-REDACTEDing-occluded-windows --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-REDACTED-pages --disable-crash-reporter --disable-default-apps --disable-dev-shm-usage --disable-hang-monitor --disable-infobars --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-REDACTEDing --disable-search-engine-choice-screen --disable-sync --enable-automation --export-tagged-pdf --force-color-profile=srgb --generate-pdf-document-outline --metrics-recording-only --no-first-run --password-store=basic --use-mock-keychain --disable-features=Translate,AcceptCHFrame,MediaRouter,OptimizationHints,RenderDocument,ProcessPerSiteUpToMainFrameThreshold,IsolateSandboxedIframes --enable-features=PdfOopif --headless=new --hide-scrollbars --mute-audio --disable-extensions --remote-debugging-port=0 --user-data-dir=/tmp/puppeteer_dev_chrome_profile-f9li5D --noerrdialogs --ozone-platform=headless --ozone-override-screen-size=800,600 --use-angle=swiftshader-webgl about:blank(dns block)- Triggering command:
/opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --disable-dev-shm-usage --use-angle=swiftshader-webgl --mute-audio --crashpad-handler-pid=5589 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/puppeteer_dev_chrome_profile-ZPS97y --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,2102261624699585542,8829259679314052806,262144 --enable-features=PdfOopif --disable-features=AcceptCHFrame,IsolateSandboxedIframes,MediaRouter,OptimizationHints,PaintHolding,ProcessPerSiteUpToMainFrameThreshold,RenderDocument,Translate --variations-seed-version(dns block)clients2.google.com
- Triggering command:
/opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --disable-dev-shm-usage --use-angle=swiftshader-webgl --mute-audio --crashpad-handler-pid=3504 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/puppeteer_dev_chrome_profile-MJK2ap --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,1838967325958793765,12419158326437385471,262144 --enable-features=PdfOopif --disable-features=AcceptCHFrame,IsolateSandboxedIframes,MediaRouter,OptimizationHints,PaintHolding,ProcessPerSiteUpToMainFrameThreshold,RenderDocument,Translate --variations-seed-version(dns block)- Triggering command:
/opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-REDACTED-timer-throttling --disable-REDACTEDing-occluded-windows --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-REDACTED-pages --disable-crash-reporter --disable-default-apps --disable-dev-shm-usage --disable-hang-monitor --disable-infobars --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-REDACTEDing --disable-search-engine-choice-screen --disable-sync --enable-automation --export-tagged-pdf --force-color-profile=srgb --generate-pdf-document-outline --metrics-recording-only --no-first-run --password-store=basic --use-mock-keychain --disable-features=Translate,AcceptCHFrame,MediaRouter,OptimizationHints,RenderDocument,ProcessPerSiteUpToMainFrameThreshold,IsolateSandboxedIframes --enable-features=PdfOopif --headless=new --hide-scrollbars --mute-audio --disable-extensions --remote-debugging-port=0 --user-data-dir=/tmp/puppeteer_dev_chrome_profile-MJK2ap --noerrdialogs --ozone-platform=headless --ozone-override-screen-size=800,600 --use-angle=swiftshader-webgl about:blank(dns block)- Triggering command:
/opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --disable-dev-shm-usage --use-angle=swiftshader-webgl --mute-audio --crashpad-handler-pid=4083 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/puppeteer_dev_chrome_profile-f9li5D --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,9800445507747328728,9185029744226650521,262144 --enable-features=PdfOopif --disable-features=AcceptCHFrame,IsolateSandboxedIframes,MediaRouter,OptimizationHints,PaintHolding,ProcessPerSiteUpToMainFrameThreshold,RenderDocument,Translate --variations-seed-version(dns block)safebrowsingohttpgateway.googleapis.com
- Triggering command:
/opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --disable-dev-shm-usage --use-angle=swiftshader-webgl --mute-audio --crashpad-handler-pid=3504 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/puppeteer_dev_chrome_profile-MJK2ap --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,1838967325958793765,12419158326437385471,262144 --enable-features=PdfOopif --disable-features=AcceptCHFrame,IsolateSandboxedIframes,MediaRouter,OptimizationHints,PaintHolding,ProcessPerSiteUpToMainFrameThreshold,RenderDocument,Translate --variations-seed-version(dns block)- Triggering command:
/opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-REDACTED-timer-throttling --disable-REDACTEDing-occluded-windows --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-REDACTED-pages --disable-crash-reporter --disable-default-apps --disable-dev-shm-usage --disable-hang-monitor --disable-infobars --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-REDACTEDing --disable-search-engine-choice-screen --disable-sync --enable-automation --export-tagged-pdf --force-color-profile=srgb --generate-pdf-document-outline --metrics-recording-only --no-first-run --password-store=basic --use-mock-keychain --disable-features=Translate,AcceptCHFrame,MediaRouter,OptimizationHints,RenderDocument,ProcessPerSiteUpToMainFrameThreshold,IsolateSandboxedIframes --enable-features=PdfOopif --headless=new --hide-scrollbars --mute-audio --disable-extensions --remote-debugging-port=0 --user-data-dir=/tmp/puppeteer_dev_chrome_profile-MJK2ap --noerrdialogs --ozone-platform=headless --ozone-override-screen-size=800,600 --use-angle=swiftshader-webgl about:blank(dns block)- Triggering command:
/opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --disable-dev-shm-usage --use-angle=swiftshader-webgl --mute-audio --crashpad-handler-pid=4083 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/puppeteer_dev_chrome_profile-f9li5D --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,9800445507747328728,9185029744226650521,262144 --enable-features=PdfOopif --disable-features=AcceptCHFrame,IsolateSandboxedIframes,MediaRouter,OptimizationHints,PaintHolding,ProcessPerSiteUpToMainFrameThreshold,RenderDocument,Translate --variations-seed-version(dns block)www.google.com
- Triggering command:
/opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --disable-dev-shm-usage --use-angle=swiftshader-webgl --mute-audio --crashpad-handler-pid=3504 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/puppeteer_dev_chrome_profile-MJK2ap --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,1838967325958793765,12419158326437385471,262144 --enable-features=PdfOopif --disable-features=AcceptCHFrame,IsolateSandboxedIframes,MediaRouter,OptimizationHints,PaintHolding,ProcessPerSiteUpToMainFrameThreshold,RenderDocument,Translate --variations-seed-version(dns block)- Triggering command:
/opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-REDACTED-timer-throttling --disable-REDACTEDing-occluded-windows --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-REDACTED-pages --disable-crash-reporter --disable-default-apps --disable-dev-shm-usage --disable-hang-monitor --disable-infobars --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-REDACTEDing --disable-search-engine-choice-screen --disable-sync --enable-automation --export-tagged-pdf --force-color-profile=srgb --generate-pdf-document-outline --metrics-recording-only --no-first-run --password-store=basic --use-mock-keychain --disable-features=Translate,AcceptCHFrame,MediaRouter,OptimizationHints,RenderDocument,ProcessPerSiteUpToMainFrameThreshold,IsolateSandboxedIframes --enable-features=PdfOopif --headless=new --hide-scrollbars --mute-audio --disable-extensions --remote-debugging-port=0 --user-data-dir=/tmp/puppeteer_dev_chrome_profile-MJK2ap --noerrdialogs --ozone-platform=headless --ozone-override-screen-size=800,600 --use-angle=swiftshader-webgl about:blank(dns block)- Triggering command:
/opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --disable-dev-shm-usage --use-angle=swiftshader-webgl --mute-audio --crashpad-handler-pid=4083 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/puppeteer_dev_chrome_profile-f9li5D --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,9800445507747328728,9185029744226650521,262144 --enable-features=PdfOopif --disable-features=AcceptCHFrame,IsolateSandboxedIframes,MediaRouter,OptimizationHints,PaintHolding,ProcessPerSiteUpToMainFrameThreshold,RenderDocument,Translate --variations-seed-version(dns block)If you need me to access, download, or install something from one of these locations, you can either:
- Configure Actions setup steps to set up my environment, which run before the firewall is enabled
- Add the appropriate URLs or hosts to the custom allowlist in this repository's Copilot coding agent settings (admins only)
Original prompt
This section details on the original issue you should resolve
<issue_title>Long sidebar jumping issue</issue_title> <issue_description>Hello, thank you for creating this plugin, it's worked smoothly for the most part when the viewport is the window/document. However, long sidebars create a jump when the viewport is a specified element.
Please note that in the following demos, the top bar's height is
100pxand the top gap is20px.
[1.] Default viewport (window/document), working great ✅ : jsfiddle.net/glenthemes/p3ejo8u1/
https://github.com/user-attachments/assets/12159918-4610-451f-b58c-07c184ba1904
[2.] Specified viewport (custom scrollable element), has a jump ❌ :
jsfiddle.net/glenthemes/qhjpfs52
https://github.com/user-attachments/assets/20b39457-3072-4cc9-836e-50c3fa610a02
Interestingly, scrolling down and sticking to the bottom seems to trigger this issue, and scrolling up has no issue.
Any help or advice is greatly appreciated, thank you! :relaxed: </issue_description>
Comments on the Issue (you are @copilot in this section)
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.