add prenav hook

This commit is contained in:
Jacky Zhao 2025-03-09 09:04:04 -07:00
parent 26ff78f81f
commit 50efa14bd2
5 changed files with 28 additions and 2 deletions

View File

@ -161,6 +161,15 @@ document.addEventListener("nav", () => {
}) })
``` ```
You can also add the equivalent of a `beforeunload` event for [[SPA Routing]] via the `prenav` event.
```ts
document.addEventListener("prenav", () => {
// executed after an SPA navigation is triggered but
// before the page is replaced
})
```
It is best practice to track any event handlers via `window.addCleanup` to prevent memory leaks. It is best practice to track any event handlers via `window.addCleanup` to prevent memory leaks.
This will get called on page navigation. This will get called on page navigation.

1
index.d.ts vendored
View File

@ -5,6 +5,7 @@ declare module "*.scss" {
// dom custom event // dom custom event
interface CustomEventMap { interface CustomEventMap {
prenav: CustomEvent<{}>
nav: CustomEvent<{ url: FullSlug }> nav: CustomEvent<{ url: FullSlug }>
themechange: CustomEvent<{ theme: "light" | "dark" }> themechange: CustomEvent<{ theme: "light" | "dark" }>
} }

View File

@ -17,7 +17,6 @@ document.addEventListener("nav", (e) => {
const observer = new IntersectionObserver((entries) => { const observer = new IntersectionObserver((entries) => {
for (const entry of entries) { for (const entry of entries) {
const parentUl = entry.target.parentElement const parentUl = entry.target.parentElement
console.log(parentUl)
if (entry.isIntersecting) { if (entry.isIntersecting) {
parentUl.classList.remove("gradient-active") parentUl.classList.remove("gradient-active")
} else { } else {

View File

@ -193,6 +193,12 @@ async function setupExplorer(currentSlug: FullSlug) {
} }
explorerUl.insertBefore(fragment, explorerUl.firstChild) explorerUl.insertBefore(fragment, explorerUl.firstChild)
// restore explorer scrollTop position if it exists
const scrollTop = sessionStorage.getItem("explorerScrollTop")
if (scrollTop) {
explorerUl.scrollTop = parseInt(scrollTop)
}
// Set up event handlers // Set up event handlers
const explorerButtons = explorer.querySelectorAll( const explorerButtons = explorer.querySelectorAll(
"button.explorer-toggle", "button.explorer-toggle",
@ -225,6 +231,13 @@ async function setupExplorer(currentSlug: FullSlug) {
} }
} }
document.addEventListener("prenav", async (e: CustomEventMap["prenav"]) => {
// save explorer scrollTop position
const explorer = document.getElementById("explorer-ul")
if (!explorer) return
sessionStorage.setItem("explorerScrollTop", explorer.scrollTop.toString())
})
document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
const currentSlug = e.detail.url const currentSlug = e.detail.url
const mobileExplorer = document.querySelector("#mobile-explorer") const mobileExplorer = document.querySelector("#mobile-explorer")

View File

@ -75,6 +75,10 @@ async function navigate(url: URL, isBack: boolean = false) {
if (!contents) return if (!contents) return
// notify about to nav
const event: CustomEventMap["prenav"] = new CustomEvent("prenav", { detail: {} })
document.dispatchEvent(event)
// cleanup old // cleanup old
cleanupFns.forEach((fn) => fn()) cleanupFns.forEach((fn) => fn())
cleanupFns.clear() cleanupFns.clear()
@ -108,7 +112,7 @@ async function navigate(url: URL, isBack: boolean = false) {
} }
} }
// now, patch head // now, patch head, re-executing scripts
const elementsToRemove = document.head.querySelectorAll(":not([spa-preserve])") const elementsToRemove = document.head.querySelectorAll(":not([spa-preserve])")
elementsToRemove.forEach((el) => el.remove()) elementsToRemove.forEach((el) => el.remove())
const elementsToAdd = html.head.querySelectorAll(":not([spa-preserve])") const elementsToAdd = html.head.querySelectorAll(":not([spa-preserve])")