As an exercise, I found a basic way of updating pages without refreshing, partially mimicking the behaviour of SPAs and routing libraries such as React Router. I was not looking for something as advanced as those libraries, but I thought it was interesting to find a quick way of doing this.
The idea is that, when clicking on an anchor within a wrapper element (#wrapper
), the content of the linked page is shown. In place of a full-page refresh/transition, a custom loader element (#loader
) is shown for a short period while the linked page's content is being loaded. The title and body of the page is replaced with those of the loaded linked page.
This way is sub-optimal. For example, one major drawback is that the back functionality does not work; the previous page does not load when navigating via the browser's back button.
To keep it minimal, I included just a few elements in the HTML snippet. The snippets will not work here because of the fetch requests, but I hope it gives an overview of the idea.
Please let me know how this could be improved. Thank you in advance!
/**
* Handle clicking links
*/
const clickAnchor = (event) => {
event.preventDefault();
const target = event.target ?? event.currentTarget;
if (!target.href) {
return;
}
loadPage(target.href);
};
/**
* Load event listeners on anchors
*/
const init = () => {
const anchors = document.querySelectorAll("#wrapper a");
for (const anchor of [...anchors]) {
const currentUrl = new URL(window.location);
const newUrl = new URL(anchor.href);
if (currentUrl.pathname === newUrl.pathname) {
// Do not load if on same page (e.g. if fragment/hash is given)
continue;
}
anchor.addEventListener("click", clickAnchor);
}
};
/**
* Determine whether to show the loader
*/
const loading = (show = true) => {
const loader = document.querySelector("#loader");
loader.classList = show ? '' : 'hidden';
};
/**
* Navigate to the given URL
*/
const loadPage = async (url) => {
loading();
const res = await fetch(url);
if (res.ok) {
const dom = new DOMParser();
const data = await res.text();
const html = dom.parseFromString(data, "text/html");
history.pushState({}, "", url);
document.title = html.title;
document.querySelector("body").innerHTML = html.body.innerHTML;
loading();
setTimeout(() => {
loading(false);
init();
}, 1000);
}
};
window.addEventListener("DOMContentLoaded", init);
#loader.hidden {
display: none;
}
#loader {
position: fixed;
inset: 0;
background-color: rgb(255, 255, 255, 0.5);
backdrop-filter: blur(5px);
display: grid;
place-items: center;
}
<div id="wrapper">
<p>Test</p>
<a href="/another-page.html">Another page</a>
</div>
<div id="loader" class="hidden">Loading…</div>
fetch
just to load the content. This will more accurately emulate SPA frameworks such as React. It will also be MUCH faster and have less security risks. \$\endgroup\$