Files
ad/molodetz/static/js/utils/ScrollMemory.js
T

65 lines
2.0 KiB
JavaScript

// retoor <retoor@molodetz.nl>
export class ScrollMemory {
static KEY = "molodetz-scroll";
static MAX_ENTRIES = 50;
static MAX_AGE_MS = 60 * 60 * 1000;
constructor() {
history.scrollRestoration = "manual";
this.key = location.pathname + location.search;
window.addEventListener("pagehide", () => this.save());
const navigation = performance.getEntriesByType("navigation")[0];
const type = navigation ? navigation.type : "navigate";
if (type === "back_forward" || type === "reload" || sessionStorage.getItem("molodetz-restore") === this.key) {
sessionStorage.removeItem("molodetz-restore");
this.restore();
}
document.addEventListener("click", (event) => {
const link = event.target.closest(".breadcrumbs a, a[data-back]");
if (link) {
sessionStorage.setItem("molodetz-restore", new URL(link.href).pathname);
}
});
}
read() {
try {
return JSON.parse(sessionStorage.getItem(ScrollMemory.KEY) || "{}");
} catch {
return {};
}
}
save() {
const entries = this.read();
entries[this.key] = { y: window.scrollY, at: Date.now() };
const now = Date.now();
const kept = Object.entries(entries)
.filter(([, value]) => now - value.at < ScrollMemory.MAX_AGE_MS)
.sort((a, b) => b[1].at - a[1].at)
.slice(0, ScrollMemory.MAX_ENTRIES);
sessionStorage.setItem(ScrollMemory.KEY, JSON.stringify(Object.fromEntries(kept)));
}
restore() {
const entry = this.read()[this.key];
if (!entry) {
return;
}
const deadline = performance.now() + 1500;
let aborted = false;
const abort = () => { aborted = true; };
["wheel", "touchstart", "keydown"].forEach((name) => window.addEventListener(name, abort, { once: true, passive: true }));
const apply = () => {
if (aborted) {
return;
}
window.scrollTo(0, entry.y);
if (Math.abs(window.scrollY - entry.y) > 2 && performance.now() < deadline) {
requestAnimationFrame(apply);
}
};
requestAnimationFrame(apply);
}
}