Single Page Apps with jQuery Routing

Single page apps are becoming the hot cake in the web industry, Everyone wants to build SPA or Single page apps. In this article, we will do a quick rundown SPA using jQuery and without using any MV* frameworks like React, Angular, Vue etc.

Single Page Apps with jQuery Routing

Overview

Since we are building a spa and we don’t want any page refreshes, we’ll use sammy.js for routing. Sammy is a 5.2K jQuery dependent library. A typical Sammy routing script is like:

var app = $.sammy(function() {

  this.get('#/', function() {
    //your function
  });
  this.get('#about/', function() {
    //your function 
  });
  this.get('#contact/', function() {
    //your function
  });
});
app.run();

first we have to initialize the application using $.sammy and stored the instance into app. In sammy we can define a route using

this.get('path/',function(){
...
});  

Each route has a callback function where we can write our logic, bind the data specific to each view/page/screen, As each route is representing a view.

After defining all routes we can bootstrap our spa using app.run();

Our blog App

To clear the concept lets jump into a somewhat more realistic example. We’re just going to make a simple blog. Which has the main page(ie, Home) and About page. In homepage or index page we’ll have a list of blogs, clicking each item will take us to blog details page. Meanwhile, you can check our demo here.

We’ll be using static JSON data blog list and we’ll use Sammy templating plugin to separate the each view.

File structure

- index.html <!-- main layout -->
- app.js <!-- stores all our routing logic -->
- css
--- style.css 
- js
--- jquery-1.11.3.min.js
--- sammy.min.js
--- sammy.template.js 
- data
--- articles.json
- templates <!-- the templates pages that will be injected into the main layout -->
--- article.template
--- article-detail.template
--- about.template

Markup

We’ll be using HTML5 boilerplate template for the markup

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script  src="js/jquery-1.11.3.min.js" type="text/javascript"></script>
  <script  src="js/sammy.min.js" type="text/javascript"></script>
  <script src="js/sammy.template.js" type="text/javascript"></script>
  <link rel="stylesheet" href="css/style.css" />
  <script src="app.js"></script>
</head>

<body>
  <div class="header-container">
    <header class="wrapper clearfix">
      <nav>
        <ul>
          <li><a href="#/">Home</a></li>
          <li><a href="#/about/">About</a></li> <!-- defining nav url according to route-->
        </ul>
      </nav>
    </header>
  </div>

  <div class="main-container">
    <div class="main wrapper clearfix">
      <div id="app">
      <!-- template will be injected here -->
      </div>
    </div>
  </div>
</body>
</html>

Defining Routes

//app.js
(function($) {

  var app = $.sammy('#app', function() {
    this.use('Template');

    this.around(function(callback) {
      var context = this;
      this.load('data/articles.json')
          .then(function(items) {
            context.items = items;
          })
          .then(callback);
    });

    this.get('#/', function(context) {
      context.app.swap('');
      $.each(this.items, function(i, item) {
        context.render('templates/article.template', {id: i, item: item})
               .appendTo(context.$element());
      });
    });
    
    this.get('#/about/', function(context) {
        var str=location.href.toLowerCase();
        context.app.swap('');
        context.render('templates/about.template', {})
               .appendTo(context.$element());
    });

    this.get('#/article/:id', function(context) {
      this.item = this.items[this.params['id']];
      if (!this.item) { return this.notFound(); }
      this.partial('templates/article-detail.template');
    });


    this.before('.*', function() {

        var hash = document.location.hash;
        $("nav").find("a").removeClass("current");
        $("nav").find("a[href='"+hash+"']").addClass("current");
   });

  });

  $(function() {
    app.run('#/about/');
  });

})(jQuery);

First, we have initialized our app in “#app” div, where we’ll inject different template according to route path. We also mentioned that we’ll be using the sammy template engine.

this.use('Template');

we have fetched blog data from articles.json using Jquery load(you can also use $.get or $.post) method and stored into the context variable.

Now time to define our for route or index page using “#/”

this.get('#/', function(context) {
  context.app.swap('');
  $.each(this.items, function(i, item) {
    context.render('templates/article.template', {id: i, item: item})
           .appendTo(context.$element());
  });
});

we already have data in context, we are looping through all the data and rendering it in article.template. we are also using context.app.swap(”); method just need to clear the content area (ie, #app) before rendering the templates.

<article>
  <section>
    <a href="#/article/<%= id %>"><h2><%= item.title %></h2></a>
  </section>
</article>

as we are using templating engine so we can write dynamic value using <%= yourdata %>. you can see we have also linked article details page for each article using #/article/<%= id %>

It will take us to article-detail.template, where we can show article image, excerpt etc.

Behind the scene, we also defined a route for article details page in app.js. we are getting article index as an URL parameter and passing the data to article-detail.template.

this.get('#/article/:id', function(context) {
  this.item = this.items[this.params['id']];
  if (!this.item) { return this.notFound(); }
  this.partial('templates/article-detail.template');
});

Similarly, we have also created a static page called “about” and rendering into about.template.

this.get('#/about/', function(context) {
    var str=location.href.toLowerCase();
    context.app.swap('');
    context.render('templates/about.template', {})
           .appendTo(context.$element());
});

Done. oops no, we forgot to bootstrap our app using app.run() where you can also mention your default route. If want to open about page first then you need to define app.run(‘#/about/’).

  $(function() {
    app.run('#/about/');
  });
  

if you want to make some operation before calling each route you can use before method. here we are selecting current nav menu according to current route path.

    this.before('.*', function() {
        var hash = document.location.hash;
        $("nav").find("a").removeClass("current");
        $("nav").find("a[href='"+hash+"']").addClass("current");
   });

So this was a very simple article on getting started with jquery routing. Now you can go ahead and create larger single page applications. One thing remember routing would need a web server running, either you can use a PHP server or node server. You can also try firefox. Chrome has blocked AJAX requests to any protocol other than http://. or https://, so if you’re just running this code on your local machine the app won’t work as the local file has link started with file://.

DownloadDemo