65 lines
2.0 KiB
JavaScript
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);
|
|
}
|
|
}
|