Navigation and History in JavaScript
Introduction
Browsers track where users go with the History API and location object. Single-page apps (SPAs) change URLs without full page reloads using history.pushState. This chapter covers reading URLs, programmatic navigation, and listening for back/forward buttons.
Prerequisites
location Object
// Current page URL parts (in browser console)
console.log(location.href);
console.log(location.pathname);
console.log(location.search);
console.log(location.hash);Navigate away (full reload):
// Assigning href reloads page
// location.href = "https://example.com/docs";Query String with URLSearchParams
// Parse ?q=node&page=2
const params = new URLSearchParams(location.search);
console.log(params.get("q"));
console.log(params.get("page"));
params.set("page", "3");
const nextSearch = params.toString();
console.log(`${location.pathname}?${nextSearch}`);history.pushState — SPA Routes
// Change URL without reload
function showView(view) {
const url = `/app/${view}`;
history.pushState({ view }, "", url);
renderView(view);
}
function renderView(view) {
document.body.dataset.view = view;
console.log("render", view);
}
window.addEventListener("popstate", (event) => {
const view = event.state?.view ?? "home";
renderView(view);
});
// Demo: push two states
showView("dashboard");
showView("settings");Framework routers (React Router, Vue Router) wrap the same History API.
history.replaceState
Like pushState but replaces current entry—good for redirects without extra back-stack entries.
navigator Snippets (Related)
// Language and online status — not navigation but common with locale
console.log(navigator.language);
console.log(navigator.onLine);
window.addEventListener("online", () => console.log("back online"));
window.addEventListener("offline", () => console.log("offline"));Geolocation is a separate permission-gated API (navigator.geolocation)—use only with user consent.
Mini Example: Hash-Based Tab Switch
<nav>
<a href="#home">Home</a>
<a href="#profile">Profile</a>
</nav>
<div id="panel"></div>
<script>
const panel = document.getElementById("panel");
function render() {
const tab = location.hash.replace("#", "") || "home";
panel.textContent = `Tab: ${tab}`;
}
window.addEventListener("hashchange", render);
render();
</script>Hash routing avoids server config for simple demos; path routing needs server fallback to index.html.
FAQ
pushState vs location.hash?
pushState gives clean URLs (/app/settings); hashes (#/settings) are easier for static hosting without rewrite rules.
Open links in new tab?
<a target="_blank" rel="noopener noreferrer"> for external URLs.
SSR frameworks?
Server must know routes too—client-only pushState is not enough alone.