Skip to content

Instantly share code, notes, and snippets.

@TeemuKoivisto
Last active October 6, 2021 08:43
Show Gist options
  • Save TeemuKoivisto/a22ba96d4eb52b95776202d5038815b7 to your computer and use it in GitHub Desktop.
Save TeemuKoivisto/a22ba96d4eb52b95776202d5038815b7 to your computer and use it in GitHub Desktop.
Very rough hack to prevent navigation in SvelteKit
let currentLocation = ''
/**
* Usage:
* @example
* import { onDestroy, onMount } from 'svelte'
* import { onNavigation } from '$utils/onNavigation'
*
* let offNavigation: () => void
*
* onMount(() => {
* offNavigation = onNavigation(() => confirm('Are you sure you want to leave?'))
* })
* onDestroy(() => {
* offNavigation()
* })
* @param fn Callback function. Probably a 'confirm' function
* @returns Clean up function
*/
export function onNavigation(fn: () => boolean): () => void {
currentLocation = window.location.href
const oldOnbeforeunload = window.onbeforeunload
const oldOnpopstate = window.onpopstate
const handler = (
oldFn: ((this: WindowEventHandlers, ev: PopStateEvent) => any) | null
) => function (this: WindowEventHandlers, ev: PopStateEvent) {
const newLocation = ev.target instanceof Window && ev.target.location.href
const success = newLocation !== currentLocation && fn()
if (!success) {
// TODO does not work when forwarding history: Alt + Right arrow
history.go(1)
} else {
oldFn && oldFn.call(this, ev)
}
}
const onbeforeunload = () => {
return 'Prevent beforeUnload'
}
const onpopstate = handler(oldOnpopstate)
window.onbeforeunload = onbeforeunload
window.onpopstate = onpopstate
const oldOnclicks: {
el: HTMLAnchorElement,
clickFn: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null
}[] = []
// TODO could do checks like this: https://github.com/PatrickG/sapper-navigation-enhancer/blob/master/src/index.ts#L42
document.querySelectorAll('a').forEach(el => {
const clickFn = el.onclick
el.onclick = function (e: MouseEvent) {
const success = fn()
if (!success) {
e.preventDefault()
} else {
clickFn && clickFn.call(this, e)
}
}
oldOnclicks.push({ el, clickFn })
})
return () => {
window.onbeforeunload = oldOnbeforeunload
window.onpopstate = oldOnpopstate
oldOnclicks.forEach(({ el, clickFn }) => {
el.onclick = clickFn
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment