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'));