Skip to content

Commit

Permalink
feat: add "touchstart" to prefetch, optimize prefetch size
Browse files Browse the repository at this point in the history
  • Loading branch information
rossrobino committed Jan 10, 2024
1 parent 37c6d89 commit d3c58e8
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 69 deletions.
5 changes: 3 additions & 2 deletions docs/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"name": "playground",
"name": "docs",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
"preview": "vite preview",
"deploy": "vc --prod"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.10",
Expand Down
2 changes: 1 addition & 1 deletion docs/src/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<meta name="description" content="Build Time Rendering Without Templates" />
</head>
<body
class="prose prose-slate mx-auto max-w-[80ch] bg-background font-humanist-classical text-foreground selection:bg-foreground selection:text-background prose-headings:font-antique prose-pre:-mx-6 prose-pre:rounded-none prose-pre:px-8 prose-pre:sm:mx-0 prose-pre:sm:rounded-md prose-pre:sm:px-4"
class="prose prose-slate mx-auto max-w-[80ch] break-words bg-background font-humanist-classical text-foreground selection:bg-foreground selection:text-background prose-headings:font-antique prose-pre:-mx-6 prose-pre:rounded-none prose-pre:px-8 prose-pre:sm:mx-0 prose-pre:sm:rounded-md prose-pre:sm:px-4"
>
<header class="not-prose flex items-center justify-between gap-6 p-6">
<a class="block font-antique text-2xl font-bold no-underline" href="/">
Expand Down
2 changes: 1 addition & 1 deletion packages/create-domco/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "create-domco",
"description": "Create a new domco project",
"version": "0.0.26",
"version": "0.0.27",
"type": "module",
"types": "./index.d.ts",
"main": "./index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-domco/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const getFiles = (options: {
},
"devDependencies": {
"@types/node": "^20.10.7",
"domco": "^0.3.6",${prettier ? `\n\t\t"prettier": "^3.1.1",` : ""}${
"domco": "^0.3.7",${prettier ? `\n\t\t"prettier": "^3.1.1",` : ""}${
prettier && tailwind
? `\n\t\t"prettier-plugin-tailwindcss": "^0.5.11",`
: ""
Expand Down
14 changes: 7 additions & 7 deletions packages/domco/docs/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Promise\<any>

#### Defined in

[types/index.ts:72](https://github.com/rossrobino/domco/blob/c9aa864/packages/domco/types/index.ts#L72)
[types/index.ts:72](https://github.com/rossrobino/domco/blob/37c6d89/packages/domco/types/index.ts#L72)

___

Expand Down Expand Up @@ -104,7 +104,7 @@ Promise\<any>

#### Defined in

[types/index.ts:23](https://github.com/rossrobino/domco/blob/c9aa864/packages/domco/types/index.ts#L23)
[types/index.ts:23](https://github.com/rossrobino/domco/blob/37c6d89/packages/domco/types/index.ts#L23)

___

Expand All @@ -129,7 +129,7 @@ Context about the current page to utilize during the build

#### Defined in

[types/index.ts:33](https://github.com/rossrobino/domco/blob/c9aa864/packages/domco/types/index.ts#L33)
[types/index.ts:33](https://github.com/rossrobino/domco/blob/37c6d89/packages/domco/types/index.ts#L33)

## Functions

Expand Down Expand Up @@ -168,7 +168,7 @@ export const build: Build = async (window) => {

#### Defined in

[helpers/addBlocks/index.ts:23](https://github.com/rossrobino/domco/blob/c9aa864/packages/domco/helpers/addBlocks/index.ts#L23)
[helpers/addBlocks/index.ts:23](https://github.com/rossrobino/domco/blob/37c6d89/packages/domco/helpers/addBlocks/index.ts#L23)

___

Expand All @@ -186,9 +186,9 @@ Can also be used more than once with different options for different selectors.
| Name | Type | Description |
| :------ | :------ | :------ |
| `options` | Object | prefetch options |
| `options.event?` | "hover" \| "load" \| "visible" | Determines when the prefetch takes place, defaults to `"hover"`. - `"hover"` - after mouseover or focus for > 200ms - `"visible"` - within viewport - `"load"` - when script is loaded, use carefully |
| `options.event?` | "hover" \| "load" \| "visible" | Determines when the prefetch takes place, defaults to `"hover"`. - `"hover"` - after `mouseover` or `focus` for > 200ms, and on `touchstart` for mobile - `"visible"` - within viewport - `"load"` - when script is loaded, use carefully |
| `options.prerender?` | boolean | Uses the experimental Speculation Rules API when supported to prerender on the client, defaults to `false`. Browsers that do not support will still use `<link rel="prefetch">` instead. [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API) |
| `options.selector?` | string | css selector for the anchor elements, defaults to elements that start with "/" `"a[href^='/']"`. For example, set to `"a[href^='/posts']"` to apply only to routes that begin with "/posts", or use another attribute entirely. |
| `options.selector?` | string | css selector for the anchor elements, defaults to elements that start with "/" `"a[href^='/']"`. For example, set to `"a[href^='/posts']"` to apply only to routes that begin with "/posts", or use another attribute entirely. If you are selecting another element instead of an `a` tag, be sure the element has a `href` property. |

#### Returns

Expand All @@ -204,4 +204,4 @@ prefetch({ prerender: true });

#### Defined in

[helpers/prefetch/index.ts:28](https://github.com/rossrobino/domco/blob/c9aa864/packages/domco/helpers/prefetch/index.ts#L28)
[helpers/prefetch/index.ts:24](https://github.com/rossrobino/domco/blob/37c6d89/packages/domco/helpers/prefetch/index.ts#L24)
113 changes: 57 additions & 56 deletions packages/domco/helpers/prefetch/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
interface SpecRules {
prerender?: {
source: string;
urls: string[];
}[];
}

const sel = "a[href^='/']";
const hover = "hover";
const defaultSelector = "a[href^='/']";
const defaultEvent = "hover";
const speculationrules = "speculationrules";

/* the current urls that have been prefetched already */
const prefetchedUrls: string[] = [];

/**
* Use on the client to prefetch/prerender the content for link tags
* on the current page.
Expand All @@ -33,6 +29,9 @@ export const prefetch = (
*
* For example, set to `"a[href^='/posts']"` to apply only
* to routes that begin with "/posts", or use another attribute entirely.
*
* If you are selecting another element instead of an `a` tag, be sure the element
* has a `href` property.
*/
selector?: string;
/**
Expand All @@ -47,90 +46,92 @@ export const prefetch = (
/**
* Determines when the prefetch takes place, defaults to `"hover"`.
*
* - `"hover"` - after mouseover or focus for > 200ms
* - `"hover"` - after `mouseover` or `focus` for > 200ms, and on `touchstart` for mobile
* - `"visible"` - within viewport
* - `"load"` - when script is loaded, use carefully
*/
event?: "hover" | "load" | "visible";
} = { selector: sel, prerender: false, event: hover },
} = { selector: defaultSelector, prerender: false, event: defaultEvent },
// above is the default if undefined
) => {
const { selector = sel, prerender = false, event = hover } = options;
// defaults if partially defined
const {
selector = defaultSelector,
prerender = false,
event = defaultEvent,
} = options;

const anchors = document.querySelectorAll<HTMLAnchorElement>(selector);

let prefetchTimer: NodeJS.Timeout;

const listener = (e: Event) => {
const { href } = e.currentTarget as HTMLAnchorElement;
const isCurrent = href === window.location.href;
if (!isCurrent) {
prefetchTimer = setTimeout(() => {
const pathname = new URL(href).pathname;
const appendTag = (href: string) => {
if (!(href === window.location.href)) {
const url = new URL(href).pathname;
if (!prefetchedUrls.includes(url)) {
// if it's not already there
prefetchedUrls.push(url);
if (
prerender &&
HTMLScriptElement.supports &&
HTMLScriptElement.supports(speculationrules)
) {
let isNew = true;
// check if it's already there
const existing = document.querySelectorAll<HTMLScriptElement>(
`script[type='${speculationrules}']`,
);
for (const s of existing) {
const rules = JSON.parse(s.textContent || "{}") as SpecRules;
if (rules.prerender?.at(0)?.urls.includes(pathname)) {
isNew = false;
break;
}
}
if (isNew) {
const specScript = document.createElement("script");
specScript.type = speculationrules;
specScript.textContent = JSON.stringify({
prerender: [
{
source: "list",
urls: [pathname],
},
],
});
document.head.appendChild(specScript);
}
} else if (
document.querySelector(`link[href='${pathname}']`) === null
) {
const specScript = document.createElement("script");
specScript.type = speculationrules;
specScript.textContent = JSON.stringify({
prerender: [
{
source: "list",
urls: [url],
},
],
});
document.head.append(specScript);
} else {
// prerender off/not supported, and it isn't already there
const link = document.createElement("link");
link.rel = "prefetch";
link.as = "document";
link.href = pathname;
document.head.appendChild(link);
link.href = url;
document.head.append(link);
}
}, 200);
}
}
};

let prefetchTimer: NodeJS.Timeout;

/**
* @param delay ms delay - for `hover`
* @returns the event listener with delay
*/
const listener =
(delay = 200) =>
(e: Event) => {
const { href } = e.currentTarget as HTMLAnchorElement;
prefetchTimer = setTimeout(() => appendTag(href), delay);
};

const reset = () => clearTimeout(prefetchTimer);

const observer = new IntersectionObserver((entries) => {
for (const e of entries) {
if (e.isIntersecting) {
listener({ currentTarget: e.target } as any);
appendTag((e.target as HTMLAnchorElement).href);
}
}
});

for (const anchor of anchors) {
if (event === hover) {
anchor.addEventListener("mouseover", listener);
anchor.addEventListener("focus", listener);
if (event === "hover") {
anchor.addEventListener("mouseover", listener());
anchor.addEventListener("mouseout", reset);
anchor.addEventListener("focus", listener());
anchor.addEventListener("focusout", reset);
anchor.addEventListener("touchstart", listener(0));
} else if (event === "visible") {
observer.observe(anchor);
} else {
// load
listener({ currentTarget: anchor } as any);
appendTag(anchor.href);
}
}
};
2 changes: 1 addition & 1 deletion packages/domco/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "domco",
"description": "Build-Time Rendering Without Templates",
"version": "0.3.6",
"version": "0.3.7",
"type": "module",
"keywords": [
"vite-plugin"
Expand Down

0 comments on commit d3c58e8

Please sign in to comment.