Creating Single Page Applications in Pure JavaScript

Nitish Kumar Singh

Dec 4, 2023

Hello developers! In this blog post, we will create a Single Page Application using pure JavaScript, HTML, and CSS. We will not use any library or framework.

This blog post is the second part of the Single Page Application blog series. Read the first blog; Understanding Single Page Applications in Pure JavaScript to learn how SPA works. In this blog, we will directly start coding.

Here, we are going to create a single-page application website for text tools. Currently, the only available tool is Words-Counter. You can visit this website at https://tools.nkslearning.com. Visit this website's GitHub repository to add more text tools and get all source codes.

Now let's starts to code. The following HTML code is the template of this website:

  <body>
    <div id="app">
      <div class="navC">
        <nav>
          <a class="navLeft" href="/" style="text-decoration: none">
            <img src="/logo.png" alt="" />
            <div>
              <span style="text-decoration: underline">TEXT</span>
              <span class="last">TOOLS</span>
            </div>
          </a>
        </nav>
      </div>
      <main></main>
    </div>
  </body>

Here, the div with the class navC is the NavBar of this website, and the main element is the main container where routing is performed.

The following JavaScript code takes care of the routing of our website:

const main = document.querySelector('main');
const linkClickHandler = (event)=>{
    event.preventDefault();
    let path = event.target.getAttribute('href') || "/"; if(!path.startsWith("/")) path = "/"+path;
    history.pushState({},null,path);
    route();
};
document.querySelector("nav a").addEventListener("click",linkClickHandler);
window.addEventListener('popstate',()=>{
    route();
});

Here, we take a reference to the main element as main, create a function named linkClickHandler that works similarly to React Link, and add a popstate event listener. Whenever we need to make a link routing-link that changes the route when clicked on, add this linkClickHandler as a click listener on this link. For example, in the above code, when the nav link, which is the logo of the website, is clicked, the Home route is routed.

What is a routing-link: This is a link (a tag) that does not load the page of the href tag; instead, it changes the route of SPA. In the above code, when this type of link is clicked, prevent the default behavior and update the browser URL using the History API and call a function route() that takes care of updating the UI. And in the popstate event listener that is fired on back and forward browser button clicks, here we directly call the route() function to sync UI state as the URL.

What is a Router: The Router is a function, class, or lines of JS code that take care of Routing (sync browser URL and UI) in a single-page application. In other words, when the URL changes externally (by back and forward buttons) and internally by routing-link click or by JS code, then it manages which route should be routed and updates the UI accordingly.

What is a Route: The Route is a function, class, or JavaScript code that takes care of updating the UI (DOM) for a specific path. For example, when the Router routes AboutRoute (function or class) for the path /about, then this code takes care of what content renders on the DOM.

The following is the code of the route() function that works as a Router:

const route = ()=>{
    let path = location.pathname;
    let pathName = path.replace(/\/+$/, path === '/' ? '/' : '');
    switch (pathName) {
        case '/': case '/index.html': Home(); break;
        case '/tools': Tools(); break;
        case '/tools/words-counter': WordCounter(); break;
        default: notFound(); break;
    }
};

In this website, we have only three pages: Home page (/ or /index.html), Tools page (/tools), Words-Counter page (/words-counter), and a default page for Not Found. This is a very simple Router, and here we are not dealing with dynamic routes. For a more advanced Router that manages too many static and dynamic routes, URL matching techniques changed.

In the above code, Home(), Tools(), WordCounter(), and notFound() are all JavaScript functions (or you can say routes) that take care of creating, rendering, and removing DOM nodes or UI.

The following is the code for the Home page:

var home;
const Home = ()=>{
    if (!home) {
        let homeUi = "<header>\
            <figure>\
                <picture>\
                    <img src='https://images.unsplash.com/photo-1626968361222-291e74711449?crop=entropy&cs=srgb&fm=jpg&ixid=M3w1MDk0Nzl8MHwxfHNlYXJjaHw3fHxjb2RlcnxlbnwwfHx8fDE3MDEzNDAzNDF8MA&ixlib=rb-4.0.3&q=85&w=1200' alt=''>\
                </picture>\
                <figcaption>Photo by <a href='https://unsplash.com/@thaliatran' rel='noopener ugc nofollow' target='_blank' style='color: inherit;'>Thalia Tran</a> on <a href='https://unsplash.com/?utm_source=medium&amp;utm_medium=referral' rel='noopener ugc nofollow' target='_blank' style='color: inherit;'>Unsplash</a></figcaption>\
            </figure>\
            <h1>Welcome to Text Tools, a Single Page Application (SPA) in Pure JavaScript</h1>\
            <p>This website serves as a showcase illustrating the construction of a single-page application using pure JavaScript. To delve into the theory and inner workings of Single Page Applications built with JavaScript, explore the following blog posts by clicking on the links below:</p>\
            <div>\
                <a class='linkBtn' target='_blank' href='https://code.nkslearning.com/blogs/understanding-single-page-applications-in-pure-javascript_656773a066999959c89a'>SPA: Theory</a>\
                <a class='linkBtn' target='_blank' href='https://code.nkslearning.com/'>SPA: Code</a>\
            </div>\
        </header>\
        <p>This website features a straightforward text utilities application. Here, you can determine the number of words, characters, spaces, and occurrences of specific words or groups of words within your text. Visit the Tools Page to get started by clicking link below:</p>\
        <a class='linkBtn' style='display: block;margin: 10px auto; width: fit-content;' href='/tools'>Explore Text Tools</a>";
        home = document.createElement("div");
        home.classList.add("home");
        home.innerHTML = homeUi;
        home.lastElementChild.addEventListener("click",linkClickHandler);
    }
    main.replaceChildren(home);
};

In the above code, I use an HTML string to create UI for the Home page. But in advanced Single Page Applications, all UI creation codes are stored as JavaScript objects or JSON or in small JS object templates. And these templates are filled by dynamic data, create nodes by createElement method of DOM, assemble these nodes and finally attach them to DOM.

Here, I only create nodes only one time and use them throughout the application life by storing them in a global variable. I use the replaceChildren method on main (it is our main container) to render page content because it automatically removes all previous children.

In a similar way, I created all routes functions that take care of rendering UI for that route. At the first time when this website script runs, to route the UI according to the URL, we call the route function at the end of the JavaScript code as below:

// after route function creation 
route();

For visiting this code website, go to https://tools.nkslearning.com and Get the full source code from the Github Repository.

I hope you learn how to create a Single Page Application and manage Routing in pure JavaScript and enjoy reading this blog post. Happy Coding!

Published on Dec 4, 2023
Comments (undefined)

Read More