Single Page Application Routing Using Hash or URL
Tags: routing, javascript, tutorial, hash
Published: 2022-03-26 20:06:59

One of the most asked code questions during a front-end interview is "Can you create a single page application with routes, without a framework?" In this tutorial, I show you how to create a custom routing system for your single page application using either the hash or URL method...without a framework.

This tutorial shows you how to build a Single Page App using vanilla JavaScript. I show you how to implement client-side routing both ways (hash or URL) in an easy-to-use format that can be replicated for any project.

## View This On YouTube
{% youtube JmSb1VFoP7w %}

## Folder Structure
We are using a basic HTML structure. You can set up your files however you like but for the sake of this tutorial, you can replicate what I have below.

```
index.html
/templates
   404.html
   index.html
   about.html
   contact.html
/js/
   router.js
```

## Let's Create Our HTML

We are going to create a basic HTML document to serve as the main page. On this page, we are going to have a nav section and a content section. You can build this out however you want but note the `<nav></nav>` tags are used for URL routing so your nav needs to be present within those tags if you are going the URL method.

```html
<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title></title>
   </head>
   <body>
      <nav>
      </nav>
      <div id="content"></div>
   </body>
</html>
``` 

## Creating the JS File

Before the `</body>` closing tag, you need to add this reference to the JS file you created above.

` <script src="/js/router.js"></script>`

## Option One: URL Routing

First, we are going to go over how to do this with **URL routing**. This means that your links will look like `/about`. This is the typical look of a URL. the hash method uses `#` to break up the pages. I will go over that further down.

_Use Case: Websites_

This option is better for SEO and is more user-friendly.

_**NOTE:** There are some downsides to using this method. You need to configure the webserver to serve the index.html for SPA Route Paths. You can do this at the server level but if you are using something like VS Code LIVE SERVER, you cannot. This means if you navigate directly to /about the server will not render the file as it needs to load the index.html scripts first. You can modify your .htaccess file to accomplish this._

### Add the HTML navigation

Add the following between the `<nav></nav>` tags in your index.html file.

```html
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
```

### Add the navigation links in HTML

Now, let's get started on the JS. First, we want to make any link in the `<nav></nav>` tags use our routing.

```js
// create document click that watches the nav links only
document.addEventListener("click", (e) => {
	const { target } = e;
	if (!target.matches("nav a")) {
		return;
	}
	e.preventDefault();
	route();
});
```

### Create the Routes

Each route will have an object array associated with it. This will tell the script what the URL reference is, as well as what template, title, and description are to be used.

```js
const routes = {
	404: {
		template: "/templates/404.html",
		title: "404",
		description: "Page not found",
	},
	"/": {
		template: "/templates/index.html",
		title: "Home",
		description: "This is the home page",
	},
	"/about": {
		template: "/templates/about.html",
		title: "About Us",
		description: "This is the about page",
	},
	"/contact": {
		template: "/templates/contact.html",
		title: "Contact Us",
		description: "This is the contact page",
	},
};
```

### Create a function that watches the URL and calls the urlLocationHandler

```js
const route = (event) => {
	event = event || window.event; // get window.event if event argument not provided
	event.preventDefault();
	// window.history.pushState(state, unused, target link);
	window.history.pushState({}, "", event.target.href);
	locationHandler();
};
```

### Create a function that handles the URL location

```js
const locationHandler = async () => {
	const location = window.location.pathname; // get the url path
	// if the path length is 0, set it to primary page route
	if (location.length == 0) {
		location = "/";
	}
	// get the route object from the urlRoutes object
	const route = routes[location] || routes["404"];
	// get the html from the template
	const html = await fetch(route.template).then((response) => response.text());
	// set the content of the content div to the html
	document.getElementById("content").innerHTML = html;
	// set the title of the document to the title of the route
	document.title = route.title;
	// set the description of the document to the description of the route
	document
		.querySelector('meta[name="description"]')
		.setAttribute("content", route.description);
};
```

### Finishing The Script

Lastly, we need to call the function when the page is first loaded, or else the home page will not work unless it is clicked on. We also need to add a watcher for the URL changes so the script knows when to show new content.

```js
// add an event listener to the window that watches for url changes
window.onpopstate = locationHandler;
// call the urlLocationHandler function to handle the initial url
window.route = route;
// call the urlLocationHandler function to handle the initial url
locationHandler();
```

## Option Two: Hash Routing

**_Please replace the content of your router.js file with the following code if you are using the hash method._**

Now, on to the second option. **Hash routing** is more common if you are using a framework but if you are creating it from scratch, the negative SEO benefit may make you shy away. This means that your links will look like `#about` instead of the typical URL method above. For some, this URL type may not be optimal due to it being so different than what your users are used to. Otherwise, the code is very similar to the URL method...even shorter.

_Use Case: Apps, Landing Pages_

_**NOTE:** There are some downsides to using this method. sing hashes may not be the best route for SEO and it may also be unusual for some users which may make them not use the website._

### Add the HTML navigation

Add the following between the `<nav></nav>` tags in your index.html file.

```html
<a href="/">Home</a>
<a href="#about">About</a>
<a href="#contact">Contact</a>
```

### Create the Routes

The hash routes look very similar to the URL routes above. You can reuse this part of the script. The difference is mainly how the route link key is defined.

```js
const routes = {
	404: {
		template: "/templates/404.html",
		title: "404",
		description: "Page not found",
	},
	"/": {
		template: "/templates/index.html",
		title: "Home",
		description: "This is the home page",
	},
	about: {
		template: "/templates/about.html",
		title: "About Us",
		description: "This is the about page",
	},
	contact: {
		template: "/templates/contact.html",
		title: "Contact Us",
		description: "This is the contact page",
	},
};
```

### Create a function that handles the URL location

```js
const locationHandler = async () => {
	// get the url path, replace hash with empty string
	var location = window.location.hash.replace("#", "");
	// if the path length is 0, set it to primary page route
	if (location.length == 0) {
		location = "/";
	}
	// get the route object from the routes object
	const route = routes[location] || routes["404"];
	// get the html from the template
	const html = await fetch(route.template).then((response) => response.text());
	// set the content of the content div to the html
	document.getElementById("content").innerHTML = html;
	// set the title of the document to the title of the route
	document.title = route.title;
	// set the description of the document to the description of the route
	document
		.querySelector('meta[name="description"]')
		.setAttribute("content", route.description);
};
```

### Finishing The Script

Again, we need to call the function when the page is first loaded, or else the home page will not work unless it is clicked on. We also need to add a watcher for the hash changes so the script knows when to show new content.

```js
// create a function that watches the hash and calls the urlLocationHandler
window.addEventListener("hashchange", locationHandler);
// call the urlLocationHandler to load the page
locationHandler();
```

## Conclusion

So while there are many ways to do this, these are the 2 you need to know to make you a better frontend developer. Once you know these, you can move on to React or Vue frameworks. So hopefully these simple yet crucial learning methods helped you pass that dreaded interview question at the beginning of the article. good luck!

Read more articles on [DevDrawer](https://thedevdrawer.com)