The history object is the browser's back-and-forward list for the current tab. Your code can move through it, and it can add its own entries without reloading the page, which is how web apps change the address and keep the Back button working. This lesson covers moving through the list, pushState, replaceState, and the popstate event.
Every page the tab visits is an entry in a list, and the browser sits at one position in it. For privacy, your code cannot read the addresses in the list. It can only count the entries and move.
| Property or method | Does |
|---|---|
history.length | how many entries the list has |
history.back() | go back one entry (same as the Back button) |
history.forward() | go forward one entry |
history.go(-2) | move by a number of entries: negative is back, positive is forward |
history.state | the data attached to the current entry (or null) |
history.back(); // one page back
history.go(-2); // two pages back
history.go(0); // reload the current page
Moving past either end of the list does nothing. These calls are asynchronous: the move happens a moment after the call, so code on the next line still runs on the current page.
console.log(typeof history.length, history.length >= 1);
console.log(history.state);
console.log(history.scrollRestoration);
history.pushState(state, "", url) adds a new entry without loading anything. The page does not reload, but the address changes and the Back button now has somewhere to go. The three arguments:
""const before = history.length;
history.pushState({ page: 2 }, "");
console.log(history.length === before + 1);
console.log(history.state);
// on a page of your own: change the address too
history.pushState({ page: 2 }, "", "/lessons/page-2.html");
Run the second example on a real page. In the Try It editor the page lives in a frame, where changing the address is blocked, so the first example leaves the URL out.
history.replaceState takes the same arguments but rewrites the current entry instead of adding one. The list does not grow. It is for fixing up the address or state of the page you are already on:
const before = history.length;
history.replaceState({ filter: "active" }, "");
console.log(history.length === before);
console.log(history.state);
When the user presses Back or Forward and lands on an entry created by pushState, the window fires a "popstate" event. event.state is the data you saved. This is how a page knows to redraw itself for the entry the user went back to:
window.addEventListener("popstate", function (event) {
console.log("went to", event.state);
// redraw the page for event.state here
});
history.go(). When your own code calls pushState you already know what changed, so you update the page yourself.
This page keeps a "page number" in the history. The buttons add entries, and the browser's Back and Forward move between them. Try it, then press Back:
<h3 id="view"></h3>
<button id="next">Next page</button>
<script>
let page = 1;
function show() { document.getElementById("view").textContent = "Page " + page; }
history.replaceState({ page: 1 }, "");
show();
document.getElementById("next").addEventListener("click", function () {
page++;
history.pushState({ page: page }, "");
show();
});
window.addEventListener("popstate", function (event) {
page = event.state ? event.state.page : 1;
show();
});
</script>
Single-page apps work this way: one real page, with pushState and popstate making it behave like many pages.
Which method adds a new entry to the history list without reloading the page?
history.back()history.pushState()history.replaceState()location.reload()pushState adds an entry. replaceState rewrites the current one, and the others move or reload.What does this log (assume history.length starts at 3)?
history.pushState({}, "");
history.replaceState({}, "");
console.log(history.length);
4. pushState grew the list from 3 to 4. replaceState changed the current entry in place, so the length stayed at 4.When does the popstate event fire?
history.go) onto a history entry. It does not fire when your own code calls pushState or replaceState.history.go(-2) after pushing three entries. Where do you land?replaceState does not change history.length