Navigating Through Client-Side Routing

Introduction

The router.js file is a crucial component of many web applications, especially those employing client-side routing to create single-page applications (SPAs). It manages the navigation within your web app, ensuring that users can seamlessly move between different views or pages without triggering full-page refreshes.

In this blog post, we'll dissect the router.js file and understand how it works, from loading routes to handling navigation and gracefully managing 404 errors. So, let's dive into the fascinating world of client-side routing!

Section 1: Loading Routes

import { loadAllPages } from './pageLoader';
import { onPopState, pushState } from './history';

The router.js begins by importing two important modules: pageLoader.js and history.js. These modules play critical roles in managing routes and history manipulation.

The loadRoutes Function

Inside the loadRoutes function, we prepare our routing setup. This function is asynchronous and primarily responsible for loading the routes our application will support. In a real-world scenario, you might use it to load pages dynamically, but for our example, we fetch HTML files representing different views.

const routes = new Map();

const loadRoutes = async () => {
  // Load your routes here
  // For example, you can load pages from loadAllPages()
  const pages = await loadAllPages();
  routes.set('/', pages.home);
  routes.set('/about', pages.about);
  routes.set('/dashboard', pages.dashboard);
};

We use the loadAllPages function from pageLoader.js to fetch these HTML files, which are then stored in a Map called routes. Each route corresponds to a specific URL path (e.g., /about, /dashboad) and its associated HTML content.

Section 2: Handling 404 Errors

The next section defines a function named handle404.

const handle404 = () => {
  const root = document.getElementById('root');
  root.innerHTML = `<div >Sorry this page is not found </div> `;
};

This function is invoked when a user tries to access a URL that doesn't match any of our predefined routes. It dynamically generates an error page within the root element of our HTML document, providing a user-friendly "404 Not Found" message.

Section 3: Navigating Between Routes

The onNavClick Function

The heart of our routing system lies in the onNavClick function. This function is designed to be called whenever a user clicks on a navigation link or manually enters a URL.

// Define and export onNavClick handler
export const onNavClick = async (pathname) => {
  const root = document.getElementById('root');
// ... continue down below

Inside onNavClick, we check if the requested pathname matches any of the routes we've defined earlier. If it doesn't, we gracefully handle the error by invoking the handle404 function.


  if (!routes.has(pathname)) {
    handle404();
    return;
  }
// ... continue down below 

 // Update the URL using the pushState function from history
  pushState({}, '', pathname);
};

If there is a match, we retrieve the corresponding page content from the routes Map and inject it into the root element of our HTML document. This seamless page change creates the illusion of navigation without a full page reload.

The handleRouting Function

To ensure that our routing system is active from the beginning, we define the handleRouting function. This function is executed on the popstate event, which is triggered whenever the user navigates using browser history (e.g., using the browser's back or forward buttons).

const handleRouting = () => {
  const pathname = window.location.pathname;
  onNavClick(pathname);
};

handleRouting extracts the current pathname from the browser's URL and calls onNavClick with this pathname, which in turn updates the content on the page.

Section 4: Initializing the Router

Finally, we define the router function, which orchestrates the entire routing process. It first loads the routes using loadRoutes, sets up the routing event listener for popstate, and triggers the initial routing using handleRouting.

const router = async () => {
  await loadRoutes();

  // Set up routing
  window.addEventListener('popstate', handleRouting);

  // Trigger initial routing
  handleRouting();
};

Conclusion

In summary, router.js is an integral part of client-side routing in web applications. It facilitates smooth navigation, loads and displays content dynamically, and handles errors gracefully. Understanding how it works empowers developers to create engaging, responsive, and efficient web applications. Whether you're building a single-page app or enhancing user experience in a traditional multi-page site, client-side routing is a valuable tool to master.

Now that we've demystified router.js, you're well-equipped to manage client-side routing in your web projects and create a seamless user experience.

Get the full code here on my Github Repository - Better Programming.
Feel Free to collaborate.

If you are happy with my content I'll be happy to connect with you on Linkedin

Happy coding!