Keep scroll position across same-screen re-renders (day nav, add food) instead of jumping to top

This commit is contained in:
4aiman 2026-09-22 11:31:37 +03:00
commit 5d24f8c3a3

View file

@ -220,6 +220,32 @@ async function init() {
// Router
// ---------------------------------------------------------------------------
// Keep the vertical scroll position across a re-render of the SAME screen (day nav, adding
// food, deletes) instead of jumping to the top. Anchor on the topmost visible top-level
// section + its offset (robust to height changes, e.g. one more food row or async charts).
function captureScrollAnchor(el) {
let top = el.scrollTop, kids = el.children, idx = 0, delta = top;
for (let i = 0; i < kids.length; i++) {
if (kids[i].offsetTop <= top + 4) { idx = i; delta = top - kids[i].offsetTop; } else break;
}
return { idx, delta };
}
function applyScrollAnchor(el, a) {
let kid = el.children[a.idx];
if (kid) el.scrollTop = Math.max(0, kid.offsetTop + a.delta);
}
// The screen fills asynchronously (charts, nutrient strips), so re-apply for a short window as
// content settles; stop early if the user scrolls themselves.
function scheduleScrollRestore(el, a) {
let stop = false, off = () => { stop = true; };
el.addEventListener("wheel", off, { passive: true, once: true });
el.addEventListener("touchmove", off, { passive: true, once: true });
let apply = () => { if (!stop) applyScrollAnchor(el, a); };
apply();
requestAnimationFrame(apply);
[60, 180, 400].forEach(ms => setTimeout(apply, ms));
}
function renderScreen(name, fromPopstate) {
// Release screen wake lock and compass listener when leaving recording screen
if (window._screenLock) { try { window._screenLock.release(); } catch {} window._screenLock = null; }
@ -243,6 +269,12 @@ function renderScreen(name, fromPopstate) {
supplements: renderSupplements,
settings: renderSettings,
};
// Re-render of the same screen: remember where we were scrolled so we can stay put.
let scrollAnchor = null;
if (prevScreen === name) {
let oldContent = app.querySelector(".screen-content");
if (oldContent && oldContent.scrollTop > 0) scrollAnchor = captureScrollAnchor(oldContent);
}
app.innerHTML = "";
// Floating recording bar (visible on all screens except recording)
@ -271,6 +303,7 @@ function renderScreen(name, fromPopstate) {
content.className = "screen-content";
app.appendChild(content);
screens[name](content);
if (scrollAnchor) scheduleScrollRestore(content, scrollAnchor);
}
function buildNav(active) {