Yes, you read the title right, Client side router for a frameworkless SPA, that’s exactly what we are going to build in this story.
Before we dive into the code, let’s first understand what is client side routing.
This is how our router would render the page —
- Listen on hashchange event.
- When the url changes, match and parse the url to the route predefined in the code.
- Look for the view to be rendered for that route, if no match is found render a 404 message.
Enough theory, show me the code already!
Below is the folder structure that we’d be working with
Let’s define a class to represent the Route in the router folder.
The Route class would take in 3 parameters while instantiating; name, path and the view associated to the route.
The setProps() method will set the props or properties that would be passed on to the route from the url and the renderView() method would return the view of the route.
The Router class will be the heart of our routing system. It will take an array of route objects as the first parameter and the renderNode where the view will be injected as the second.
Our Router class will have methods like match() and navigate() to match the routes and navigate respectively. Lets look at their definitions.
The navigate() method will filter through each route and check if it matches with any of the predefined routes. If no route is matched the renderNode will render a 404 message, else it would render the matched route’s view;
In the match(), we are creating regular expression of the route path and matching it with the requested path. If it matches we are also finding out if the requested route has any route parameters.
Well you might have already noticed the problem in the navigation(), we are literally injecting the view using innerHTML, which btw must be avoided at all cost. Mainly because of security reasons regarding XSS and also another problem that I encountered while coding, that any element in the injected view does not have an event listener attached to it. So that means you cannot have a button in a view to navigate to another route, it simply won’t work. To get around this we need to build the view using document.createElement(). To keeps this post concise, I’m leaving it upto you to figure it out(I may explore it later).
I like to add utility functionalities so lets define another method to add routes to the router.
Let’s take a look at the index.js of router.
Here we are exporting a function which takes routes as the parameter and creates an instance of the Router Class. The renderNode is a div with an id of app. We are adding the click event listener to the buttons that have a route attribute (inspired from other libraries), and listening for the hashchange event on the url to call the navigate() method.
Now, lets take a look at all the views in the application (look at the captions if you get lost keeping tracks of files)
You can install html pragmas in your code editor for syntax highlighting the html in template literals.
And finally in our app.js file lets define all our routes and pass it to our router.
The following code would go in the body of the index.html file
Make sure to add the type=”module” in the script tag in order to use the import/export es6 feature, because of this we don’t have to setup any extra configuration.
To wrap this up lets add a few lines of CSS.
To conclude this post, I’d say that you don’t need to bring in other libraries for small and simple Single Page Application, you can spin up you own custom solution, I just happen to show you one of the many ways, probably you can do more and much better. If you do, do let me know, I’d be happy to see your work.
I referred to this video on YouTube for writing the story — https://www.youtube.com/watch?v=D1fLaNxd-ZM
Cheers and happy coding 🍻😉
This post was originally shared on medium. Check out my medium profile @vijit2ail