From 108f40feee5df5204faa715e03e04ba87bb02747 Mon Sep 17 00:00:00 2001 From: Jeff Delaney Date: Wed, 31 Aug 2022 20:28:49 -0700 Subject: [PATCH] scroll to id on link --- example/index.html | 2 +- lib/handlers.ts | 20 ++++++++++++++------ lib/interfaces.ts | 1 + lib/router.ts | 9 +++++---- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/example/index.html b/example/index.html index 4f5aaeb..0b6b2bd 100644 --- a/example/index.html +++ b/example/index.html @@ -29,7 +29,7 @@

Home

- Home | About | + Home | About
diff --git a/lib/handlers.ts b/lib/handlers.ts index 8ad0ead..738ead4 100644 --- a/lib/handlers.ts +++ b/lib/handlers.ts @@ -1,12 +1,18 @@ import { RouteChangeData } from './interfaces'; /** - * @param {} type - * scroll to top of page + * @param {string} type + * @param {string} id + * scroll to position on next page */ -export function scrollToTop(type: string): void { +export function scrollTo(type: string, id?: string): void { if (['link', 'go'].includes(type)) { - window.scrollTo({ top: 0 }); + if (id) { + const el = document.querySelector(id); + el ? el.scrollIntoView({ behavior: 'smooth', block: 'start' }) : window.scrollTo({ top: 0 }); + } else { + window.scrollTo({ top: 0 }); + } } } /** @@ -16,7 +22,7 @@ export function scrollToTop(type: string): void { */ export function fullURL(url?: string): string { const href = new URL(url || window.location.href).href; - return href.endsWith('/') || href.includes('.') ? href : `${href}/`; + return href.endsWith('/') || href.includes('.') || href.includes('#') ? href : `${href}/`; } /** @@ -92,11 +98,13 @@ export function handleLinkClick(e: MouseEvent): RouteChangeData { return { type: 'scrolled' }; } + // ID to scroll to after navigation, like /route/#some-id + const scrollId = ahref.match(/#([\w'-]+)\b/g)?.[0]; const next = fullURL(url.href); const prev = fullURL(); // addToPushState(next); - return { type: 'link', next, prev }; + return { type: 'link', next, prev, scrollId }; } else { return { type: 'noop' }; } diff --git a/lib/interfaces.ts b/lib/interfaces.ts index 8a5d250..7e6c0ca 100644 --- a/lib/interfaces.ts +++ b/lib/interfaces.ts @@ -17,6 +17,7 @@ export interface RouteChangeData { type: 'link' | 'popstate' | 'noop' | 'disqualified' | 'scroll' | 'go' | string; next?: string; prev?: string; + scrollId?: string; } export type FlameWindow = Window & typeof globalThis & { flamethrower: Router }; diff --git a/lib/router.ts b/lib/router.ts index bd66a76..2504e40 100644 --- a/lib/router.ts +++ b/lib/router.ts @@ -1,5 +1,5 @@ import { FetchProgressEvent, FlamethrowerOptions, RouteChangeData } from './interfaces'; -import { addToPushState, handleLinkClick, handlePopState, scrollToTop } from './handlers'; +import { addToPushState, handleLinkClick, handlePopState, scrollTo } from './handlers'; import { mergeHead, formatNextDocument, replaceBody, runScripts } from './dom'; const defaultOpts = { @@ -158,7 +158,7 @@ export class Router { * @param {RouteChangeData} routeChangeData * Main process for reconstructing the DOM */ - private async reconstructDOM({ type, next, prev }: RouteChangeData): Promise { + private async reconstructDOM({ type, next, prev, scrollId }: RouteChangeData): Promise { if (!this.enabled) { this.log('router disabled'); return; @@ -222,6 +222,7 @@ export class Router { }); }) .then((stream) => new Response(stream, { headers: { 'Content-Type': 'text/html' } })); + const html = await res.text(); const nextDoc = formatNextDocument(html); @@ -235,14 +236,14 @@ export class Router { transition.start(() => { replaceBody(nextDoc); runScripts(); + scrollTo(type, scrollId); }); } else { replaceBody(nextDoc); runScripts(); + scrollTo(type, scrollId); } - // handle scroll - scrollToTop(type); window.dispatchEvent(new CustomEvent('flamethrower:router:end'));