Skip to content

SPA Router is no framework Vanilla JS single page application router solution

License

Notifications You must be signed in to change notification settings

sassyelements/spa-router

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SPA Router

SPA Router is a (no framework) vanilla JS alternative for Single Page Application. It's a plugin and can be imported as a module. It supports both pathnames and hashes.

Demo

To try out SPA Router clone this repo and run the following commands.

First install npm packages

npm install

Then run

npm start

# output
> spa router@1.0.0 start
> parcel index.html

Server running at http://localhost:1234
Built in 1.60s

Visit http://localhost:1234 to see the result. Your port may not be 1234! Before you visit localhost you must copy templates/ directory into dist/ directory because Parcel doesn't do it.

How to Use SPA Router

First you need to get the plugin file SPARouter.js. Copy it to your JS directory. You can create a directory called plugins/ in your JS directory and put the SPARouter.js there.

In your index.js file you want to import SPARouter as shown in the example below:

import router from './js/plugins/SPARouter';

After the plugin is imported you then want to define the routes. As the plugin supports both pathnames and hashes the routes objects are slightly different. Pathnames uses / and hashes obviously uses #.

Your index.html File

Your index.html file must either have a main tag or a div or any other element with a class called main. This is the main container where HTML from templates is loaded. See the example below how it should look like.

[...]

<body>
    <main class="main"></main>
</body>

[...]

URL Routes Object

const urlRoutes = {
    404: {
        template: './templates/404.html',
        title: '404 Not Found',
        description: 'This is the 404 page.'
    },
    '/': {
        template: './templates/home.html',
        title: 'Home Page',
        description: 'This is the home page.'
    },
    '/about': {
        template: './templates/about.html',
        title: 'About',
        description: 'This is the about page.'
    },
    '/contact': {
        template: './templates/contact.html',
        title: 'Contact',
        description: 'This is the contact page.'
    }
};

Hash Routes Object

const hashRoutes = {
    404: {
        template: './templates/404.html',
        title: '404 Not Found',
        description: 'This is the 404 page.'
    },
    '/': {
        template: './templates/home.html',
        title: 'Home Page',
        description: 'This is the home page.'
    },
    '#about': {
        template: './templates/about.html',
        title: 'About',
        description: 'This is the about page.'
    },
    '#contact': {
        template: './templates/contact.html',
        title: 'Contact',
        description: 'This is the contact page.'
    }
};

Initialize Router

After the routes are defined it's time to initialize the router. There are also two different scenarios. For the URL based routes you need to specify the DOM selector. See the example below also the HTML!

router.setRoutes(urlRoutes).initRouter({ selector: 'nav a' });

HTML

<nav class="navigation nav">
    <div class="nav__nav">
        <ul class="nav__list">
            <li class="nav__item">
                <a class="nav__link lead" href="/">Home</a>
            </li>
            <li class="nav__item">
                <a class="nav__link lead" href="/about">About</a>
            </li>
            <li class="nav__item">
                <a class="nav__link lead" href="/contact">Contact</a>
            </li>
        </ul>
    </div>
</nav>

If you wanna use the hash based routes then you don't have to specify the selector you can set it to null as in the following example.

router.setRoutes(urlRoutes).initRouter({ selector: null });

HTML

<nav class="navigation nav">
    <div class="nav__nav">
        <ul class="nav__list">
            <li class="nav__item">
                <a class="nav__link lead" href="/">Home</a>
            </li>
            <li class="nav__item">
                <a class="nav__link lead" href="#about">About</a>
            </li>
            <li class="nav__item">
                <a class="nav__link lead" href="#contact">Contact</a>
            </li>
        </ul>
    </div>
</nav>

Finally

In your index.js the whole router settings will look like this:

import router from './js/plugins/SPARouter';

const urlRoutes = {
    404: {
        template: './templates/404.html',
        title: '404 Not Found',
        description: 'This is the 404 page.'
    },
    '/': {
        template: './templates/home.html',
        title: 'Home Page',
        description: 'This is the home page.'
    },
    '/about': {
        template: './templates/about.html',
        title: 'About',
        description: 'This is the about page.'
    },
    '/contact': {
        template: './templates/contact.html',
        title: 'Contact',
        description: 'This is the contact page.'
    }
};

router.setRoutes(urlRoutes).initRouter({ selector: 'nav a' });

Custom Events

SPA Router provides one custom event called locationChange. This event is listening for any changes that happen when the URL is changed. It can come in handy when we want to detect those changes and do something with the DOM. The History API doesn't provide any event that listens for state changes e.g pushState and replaceState. There are many questions on StackOverflow like this question about detecting the changes in URL. However all the solutions there are either some hacks or not very helpful at least to me. Many of the things I couldn't even understand but with custom events we can solve this problem very easily.

As the SPA Router is all about path and hash changes we can add it to window and listen for the location changes.

For example we want to add an element to the DOM and remove it when we are on a certain URL. Let's say when the 404 page is loaded we want to add a div with a message Our Server is a Loser!. When the user is back on home page we want to remove that message.

window.addEventListener(`locationChange`, (event) => {
    // First extract the pathname
    const path = event.target.location.pathname;
    // Select the body element
    const body = document.querySelector('body');
    // Assign the HTML element to message
    const message = `<div id="404-message">Our Server is a Loser!</div>`;

    // Check if we are on 404 page also add another condition to our if clause
    // to make sure we do not add the same element over and over again
    if (path === '/404' && body.lastChild.id !== '404-message') {
        // Insert the element to the body after all other elements as a
        // last child
        body.insertAdjacentHTML('beforeend', message);
    }

    // Select the message element that we added
    const messageElem = document.querySelector('#404-message');
    // Check if we are on the home page and message element exist
    if (path === '/' && messageElem) {
        // Either use remove() method or removeChild() both will work
        // messageElem.remove();        
        body.removeChild(body.lastChild);
    }
});

About

SPA Router is no framework Vanilla JS single page application router solution

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published