Photo by Rubaitul Azad on Unsplash
How to Implement Your Own Browser History Functionality
Design your browser history with a doubly linked list
At the heart of your web browser's history functionality lies one of the most important data structures: a linked list.
Web browsers usually use a doubly linked list to implement the browsing history functionality, since it allows them to navigate between all the visited pages using the back and forward buttons. Every time you visit a new URL, it's stored as a node whose next pointer points to the next URL you visit, and also contains references to previous nodes that are part of the list. In this article, we're going to replicate a very simplified version of the functionality and implement a doubly linked list that keeps track of your browsing history.
Why Use a Doubly Linked Lists?
There are many benefits of using a doubly linked list to implement a browser's browsing history. In addition to seamless navigation in both directions, it's also easy to insert and delete nodes at any position within the list.
Of course, you can use a singly linked list to store the browser history, but you'll have to deal with additional memory overhead and operations to traverse the linked list backwards. With a doubly linked list, you can navigate in both directions without extra complexity or operations.
Implementing Browsing History Functionality
To get started, we first need to implement a node class & a doubly linked list class. Let's start with the node class:
class Node {
constructor(data) {
this.data = data;
this.previous = null;
this.next = null;
}
}
And then the doubly linked list class. In this case, we will use a current pointer to keep track of the current page we're on.
class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null;
this.current = null;
}
With that out of the way, we can now start thinking about the functionalities we need. We basically need three things:
The ability to add a new page to our history
The ability to navigate to the next page in the history
The ability to navigate to the previous page in the history.
Let's implement those one by one.
Add New Page
Initially, if there are no pages in the list, we'll set the head, tail, and the current pointers to point to newNode. If some nodes already exist in the list, then we'll have to set the pointers a little differently using the current tail. We'll first set the newNode's previous to the current tail, and then set the tail's next pointer to point to the newNode. We'll then update the tail to point to the newNode.
If this sounds confusing, here's a visualization of how the list will look like if we add two new pages, Home & About:
And here's the code:
addPage(page) {
const newNode = new Node(page);
if (this.head === null) {
this.head = newNode;
this.tail = newNode;
this.current = newNode;
} else {
newNode.previous = this.tail;
this.tail.next = newNode;
this.tail = newNode;
}
}
Navigate Forward
Navigating forward is really easy to understand. All you have to do is check to make sure you're not at a null pointer and the node you want to traverse to is also not null. If both are not null, we'll just update our current pointer to the next node. And to see that in effect, we're going to print the current node. If there's no node to traverse to, we'll print 'No next page in the browser history.'
navigateForward() {
if (this.current !== null && this.current.next !== null) {
this.current = this.current.next;
console.log("Navigating forward to the next page:", this.current.data);
} else {
console.log("No next page in the browser history.");
}
}
Navigate Backward
Navigating backward also uses the same logic - you check to make sure you're not at a null pointer and the node you want to traverse to is also not null. If both are not null, update our current pointer to current's previous (it might sound confusing but refer to the diagram above to understand better). Finally, we'll print something for demonstration.
navigateBack() {
if (this.current !== null && this.current.previous !== null) {
this.current = this.current.previous;
console.log("Navigating back to the previous page:", this.current.data);
} else {
console.log("No previous page in the browser history.");
}
}
Display Browser History
To see the functionality at work, we need another function that'll display the current browser history. Nothing fancy happens here; the code just prints the history and highlights the current page by surrounding it with square brackets (example: [Home]). We'll see this in action in just a bit.
displayHistory() {
let current = this.head;
let history = "";
while (current !== null) {
if (current === this.current) {
history += "[" + current.data + "] -> ";
} else {
history += current.data + " -> ";
}
current = current.next;
}
history += "null";
console.log("Browser History: ", history);
}
}
Example Usage
That's pretty much all the code you can need to implement your own browser history functionality. Let's see that in action.
First, let's add some pages:
const browserHistory = new DoublyLinkedList();
browserHistory.addPage("Home");
browserHistory.addPage("About");
browserHistory.addPage("Products");
browserHistory.addPage("Contact");
And view what the history looks like:
browserHistory.displayHistory();
Here's the output:
Remember that [Home] means that the current page is the Home page. Let's now try navigating backwards from Home:
browserHistory.navigateBack();
As expected, this is the output:
Let's navigate to the end of the list and see the current page we're at:
browserHistory.navigateForward();
browserHistory.navigateForward();
browserHistory.navigateForward();
browserHistory.displayHistory()
Here's the output:
You should now be able to navigate back, too:
browserHistory.navigateBack();
browserHistory.displayHistory();
And that's all! This was a step-by-step tutorial of implementing your web browser's browsing history functionality. I've also added the entire code on my GitHub or you can simply run it online on OneCompiler (hassle-free and works great without requiring any sign up).