History
The window.history
refers to the history object that stores a list of visited documents. The back()
and forward()
methods behave like back and forward buttons in a browser. The go()
method can load any page in the history.
history.back();
history.forward();
history.go(-2); // Go back 2
Nowadays, web applications load content dynamically without requesting new pages from the server. To manage browser's history dynamically, use the pushState()
and replaceState()
methods of the history object. They allow you to add, modify, and handle history entries without causing a page reload.
The pushState()
method adds a new entry to the browser's session history stack. The first argument to pushState()
method is an object that contains data required to restore the current state of the application. The object is saved using the structured clone algorithm, which is more robust than JSON.stringify()
and supports maps, sets, date objects, as well as typed arrays and regular expressions. However, functions and classes are not supported.
The second argument is a title that is ignored by the browsers, therefore you should just pass an empty string. The third argument is a URL string that will be displayed in the location bar. This URL allows the user to bookmark the state of your application.
history.pushState({ page: 1 }, "", "/page1");
The replaceState()
method modifies the current history entry instead of adding a new one. When an application that uses pushState()
is first loaded, you should initialize the state of the application by calling replaceState()
method.
history.replaceState({ page: 1 }, "", "/page1");
The "popstate" event is fired when the active history entry changes. This can happen when the user clicks the browser's back or forward buttons, or when history.back()
, history.forward()
, or history.go()
is called. The event object has a property named state
, which contains a structured clone of the current state of the application (that you passed to pushState()
or replaceState()
methods).
window.addEventListener('popstate', function(event) {
console.log('State:', event.state);
});
Suppose we have a blog site with a list of articles on the main page. Clicking on an article title loads the article content without reloading the page. We want the URL to reflect the current article, and the browser's back and forward buttons to navigate between articles.
<body>
<nav>
<h2>Articles</h2>
<ul>
<li><a href="#" data-article-id="1">Article 1</a></li>
<li><a href="#" data-article-id="2">Article 2</a></li>
<li><a href="#" data-article-id="3">Article 3</a></li>
<li><a href="#" data-article-id="4">Article 4</a></li>
</ul>
</nav>
<main></main>
</body>
We'll use pushState()
to update the URL when users navigate within the application, and handle "popstate" event to manage back and forward navigation. Upon initial load of the document, we'll use replaceState()
to initialize the state of the application.
const articles = [
{
id: 1,
title: "Article 1",
content: "Lorem ipsum dolor sit amet, consectetur adipisicing elit.",
},
{
id: 2,
title: "Article 2",
content: "Lorem ipsum dolor sit amet, consectetur adipisicing elit.",
},
{
id: 3,
title: "Article 3",
content: "Lorem ipsum dolor sit amet, consectetur adipisicing elit.",
},
];
const nav = document.querySelector("nav");
const main = document.querySelector("main");
function loadArticle(id) {
const article = articles.find((article) => article.id === id);
if (article) {
main.innerHTML = `<h1>${article.title}</h1><div>${article.content}</div>`;
} else {
main.innerHTML = `<h1>Not Found</h1><div>Article not found!</div>`;
}
}
// Handle navigation to an article
nav.addEventListener("click", (e) => {
e.preventDefault();
if (e.target.tagName === "A") {
const id = Number(e.target.dataset.articleId);
history.pushState({ id }, "", `/articles/${id}`);
loadArticle(id);
}
});
// Handle backward and forward navigation
window.addEventListener("popstate", (e) => {
if (e.state?.id) {
loadArticle(e.state.id);
} else {
main.innerHTML = "<p>Select an article from the list.</p>";
}
});
// Initial load
const match = window.location.pathname.match(/\/articles\/(\d+)/);
if (match) {
const id = Number(match[1]);
history.replaceState({ id }, "", `/articles/${id}`);
loadArticle(id);
} else {
main.innerHTML = "<p>Select an article from the list.</p>";
}