Preloading, measured
Hover a link and the page is fetched before you click. A log shows what came from the cache and what came from the network.
The Preload plugin fetches a page when the pointer rests on its link, and puts it in the cache. By the time of the click there is nothing left to wait for. The log in this demo shows it happening.
main.js
// Hovering a link fetches its page and puts it in the cache before the click
const pagefold = new Pagefold({ plugins: [new PagefoldPreload()] })
const lines = document.querySelector('#lines')
const path = (url) => url.split('/').pop()
const log = (text, kind) => {
const li = document.createElement('li')
li.className = kind
li.textContent = text
lines.prepend(li)
}
// A page was fetched ahead of the click
pagefold.hooks.on('page:preload', (visit, { page }) => log(`preloaded ${path(page.url)}`, 'pre'))
// A visit: was the page already in the cache, and how long did it take to have it?
let started = 0
let cached = false
pagefold.hooks.on('visit:start', (visit) => {
started = performance.now()
cached = pagefold.cache.has(visit.to.url)
})
pagefold.hooks.on('page:load', (visit) => {
const ms = Math.round(performance.now() - started)
log(`opened ${path(visit.to.url)} · ${cached ? 'from the cache' : 'from the network'} · ${ms} ms`, cached ? 'hit' : 'miss')
})
// Forget everything, as you would after a login or a change of language
document.querySelector('#clear').addEventListener('click', () => {
pagefold.cache.clear()
log('cache emptied', 'pre')
}) index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Home · Preload</title>
<meta name="description" content="Hover first, then click">
<link rel="stylesheet" href="../_lib/fade.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="top">
<strong>Preload</strong>
<nav>
<a href="index.html">Home</a>
<a href="pricing.html">Pricing</a>
<a href="docs.html">Docs</a>
<a href="blog.html">Blog</a>
<a href="team.html">Team</a>
</nav>
</header>
<main id="pagefold" class="transition-fade">
<h1>Home</h1>
<p class="lead">Hover first, then click. Rest the pointer on a link for a moment and then click it.</p>
<div class="tiles">
<a class="tile" href="pricing.html"><b>Pricing</b><span>pricing.html</span></a>
<a class="tile" href="docs.html"><b>Docs</b><span>docs.html</span></a>
<a class="tile" href="blog.html"><b>Blog</b><span>blog.html</span></a>
<a class="tile" href="team.html"><b>Team</b><span>team.html</span></a>
</div>
</main>
<section id="log" aria-label="What Pagefold did">
<header><b>Log</b><button type="button" id="clear">Empty the cache</button></header>
<ol id="lines"></ol>
</section>
<script src="../_lib/pagefold.js"></script>
<script src="../_lib/preload.js"></script>
<script src="main.js"></script>
</body>
</html> style.css
*{box-sizing:border-box}
html,body{min-height:100%}
body{margin:0;font:16px/1.6 system-ui,sans-serif;background:#0a0a0a;color:#fafaf8}
/* Links: the accent color with a quiet underline that gets stronger on hover */
a{color:#5af0ae;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:.25em;text-decoration-color:rgba(90,240,174,.5);transition:color .15s,text-decoration-color .15s}
a:hover{color:#00ff9c;text-decoration-color:currentColor}
a:focus-visible{outline:2px solid #5af0ae;outline-offset:3px;border-radius:2px}
.top{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.8rem 1.5rem;border-bottom:1px solid #2c2c2c}
.top strong{font:700 .95rem ui-monospace,Menlo,monospace}
.top nav{display:flex;gap:1.25rem;flex-wrap:wrap}
main{padding:2rem 1.5rem 3rem;max-width:46rem}
h1{margin:0 0 .5rem;font-size:2.1rem;line-height:1.15}
p{margin:0 0 1rem;max-width:36rem;color:#c9c9c4}
.lead{color:#a3a39e;font-size:1.05rem}
.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:.75rem;margin-top:1.5rem}
.card{border:1px solid #2c2c2c;background:#141414;border-radius:12px;padding:.9rem}
.card i{display:block;height:3.2rem;border-radius:8px;margin-bottom:.6rem}
.card b{display:block;font-size:.95rem}
.card span{font-size:.82rem;color:#a3a39e}
@media (max-width:34rem){.cards{grid-template-columns:1fr}main{padding:1.25rem}}
.tiles{display:grid;grid-template-columns:repeat(2,1fr);gap:.75rem;margin-top:1.25rem}
.tile{display:block;padding:1rem;border:1px solid #2c2c2c;border-radius:12px;background:#141414;color:#fafaf8;text-decoration:none}
.tile:hover{border-color:#00ff9c;background:#1a1a1a}
.tile b{display:block}
.tile span{font:.78rem ui-monospace,Menlo,monospace;color:#a3a39e}
main{padding-bottom:1rem}
#log{margin:0 1.5rem 1.5rem;border:1px solid #2c2c2c;border-radius:12px;background:#101010;max-width:46rem;font:.8rem ui-monospace,Menlo,monospace}
#log header{display:flex;justify-content:space-between;align-items:center;padding:.5rem .8rem;border-bottom:1px solid #2c2c2c}
#log button{font:inherit;color:#fafaf8;background:#1a1a1a;border:1px solid #2c2c2c;border-radius:8px;padding:.25rem .6rem;cursor:pointer}
#log button:hover{border-color:#00ff9c}
#lines{list-style:none;margin:0;padding:.4rem .8rem;max-height:9.5rem;overflow:auto}
#lines li{padding:.12rem 0;color:#a3a39e}
#lines .hit{color:#00ff9c}
#lines .miss{color:#ff9f5c}
#lines .pre{color:#7cc4ff} What to try
- Rest the pointer on a tile, wait for a moment and look at the log:
preloaded pricing.html. - Now click it. The log says
opened pricing.html · from the cache, with about 1 ms. - Click a tile without hovering it first, for example with the keyboard. The log says
from the network, and the time is longer. - Press Empty the cache and try again. Emptying it is what you would do after a login or a change of language.
How it works
const pagefold = new Pagefold({ plugins: [new PagefoldPreload()] })
pagefold.hooks.on('page:preload', (visit, { page }) => log(`preloaded ${page.url}`))
page:preloadfires for every page the plugin fetches. The log lines come from it.- The cache is Pagefold’s own. A preloaded page is stored in it like any other, which is why the visit finds it. It works with any server, with no special headers.
- The times are small here because this site runs on a fast connection. On a slower one the difference between the cache and the network is much bigger.
More in Preload and Caching and history.