feat: preserve scroll position across case-action redirects

Clicking close/reopen or any bulk form submits a POST and follows the
303 to the listing, which is a fresh navigation — browsers do not
restore scroll on navigation, only on reload(). A user scrolled to
row 30 of a long list loses focus and has to scroll back manually.

Fix: small JS on my_cases.html that stashes window.scrollY in
sessionStorage before every form submit and restores it on the next
page load. Silent no-op when sessionStorage is blocked (strict
privacy mode), so the feature is purely additive.

No server changes — kept the redirect URLs clean. An earlier iteration
with a #case-<uuid> anchor on the redirect turned out to race with
the explicit scrollTo on any page where the referer scrollY was near
0; dropping the anchor made the JS the sole source of truth.
This commit is contained in:
2026-04-21 12:01:51 +02:00
parent 8b1f58ba23
commit 5255880d78
+32
View File
@@ -160,6 +160,38 @@ try {
});
})();
// Preserve scroll position across POST-redirects (close, reopen,
// bulk-*). Browsers keep scroll on location.reload() but NOT on a
// redirect-driven navigation, even when the target URL matches the
// referer. Without this, clicking close on row 30 of a long list
// dumps the user back at scrollY=0 with the action-row off-screen.
//
// Mechanism: capture scrollY in sessionStorage before every form
// submit on this page, restore it on the very next load, then delete.
// Using the classic two-arg scrollTo(0, y) form — the options-object
// variant is silently ignored in some older browsers.
(() => {
const STASH = 'doctate:scrollY';
try {
document.querySelectorAll('form').forEach((f) => {
f.addEventListener('submit', () => {
sessionStorage.setItem(STASH, String(window.scrollY));
});
});
const stashed = sessionStorage.getItem(STASH);
if (stashed !== null) {
sessionStorage.removeItem(STASH);
const y = parseInt(stashed, 10);
if (!isNaN(y)) {
window.scrollTo(0, y);
}
}
} catch (_) {
// sessionStorage may be blocked (strict privacy mode) — silent fail,
// scroll returns to 0 like it did before this feature.
}
})();
// Live-update bus: any case event for this user triggers a debounced
// page reload. Debounce collapses event bursts (e.g. bulk-delete) into
// one reload. EventSource auto-reconnects on network drops.