Code Self Study Forum

A client side routing work around for Vanilla JS web apps

To build client-side routing you need to understand both the popstate and pushstate functions.

In short, push state or simply push will add a URL to the user’s browser history, even if they didn’t visit that page. Or in other words, if the view was created client side.
Popstate can be watched for when the user clicks the back button.

So take a simple example of creating a recipe.

You start with peas, which is sever side rendered. You then add carrots. The addition of carrots is rendered client side after fetching the data from AJAX or the API.

You then use push to add peas and carrots to the users browser history.

If the user clicks back, you use popstate to reload the server side rendered page on peas.

Let’s look at this example in code:

Server side render peas with the following URL:"Peas"

Then add carrots with AJAX/Fetch and push the URL

window.history.pushState("string", "Title", "/food=Peas+Carrots)

Then when you click back listen to popstate to load just Peas again

window.addEventListener('popstate', (event) => {
window.location.href = document.location;

The page:"Peas"
Is now loaded again.

The way the above code works is to listen for popstate. That basically listens for a back button click. Then simply redirect the user to the URL in the browser history. Or load the page from there.

I hope that all makes sense, but post questions below if not.


This turned out to become more complicated since I also used hashes to control menu options that slide in and out on click and on back button behavior.

So here is my work around:

//Listen for any changes in the URL (including back button and more)
window.addEventListener('popstate', (event) => {

//Useful items from the current window location that is loaded with back
    //window.location.href = document.location; //The whole URL
    // console.log(window.location.protocol); // Https or http
    // console.log(window.location.hostname); // the sub-domain and domain
    // console.log(window.location.pathname); // The file path
    // console.log(; // Any Query Parameters
    console.log(window.location.hash); //The hash

    /*Logic of routing:
    1. Check for menu hash, if is there, the user is on a menu page and you should do nothing (No page load from the popState change)
    2. Use DOM elements to check if menu windows are open in mobile, if so, close them. (Hash still needed so URL does not go back)
    3. If no navigation panes are open then load the url in the page as normal
//Getting hashs
if (window.location.hash==='#foodlist' || window.location.hash==='#menu') 
    console.log("Hash here - do nothing");
    //Do Nothing
  /*In this case, the user can also make entries that affect the state.
 So a separate function will log all the URL changes in an array, to be pushed to the history API once the user closes the mobile menu*/
else if (document.getElementById("foodlistwrap").className == "foodlistwrapM") { 
    console.log("Router Close Menu");
    //Just close the menu if it is open
else if (document.getElementById("nshow").classList=="mobilenavshow mobile_menu_width") //Check for another menu being open
//Change the DOM to hide the menu
console.log("Router Load URL");
//Just load the previous state that is in the URL parameters
window.location.href = document.location;

    }); //Close popState listener