Skip to content

Instantly share code, notes, and snippets.

@maxweisel
Created June 28, 2012 23:35
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save maxweisel/3014727 to your computer and use it in GitHub Desktop.
Save maxweisel/3014727 to your computer and use it in GitHub Desktop.
collect.js - use clean urls with Backbone History / Router

clickify.js intercepts all internal links and passes them to Backbone History/Router

Installation

<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>

<!-- backbone.js -->
<script src="/path/to/backbone.js"></script>

<!-- clickify.js -->
<script src="https://raw.github.com/gist/3014727/clickify.js"></script>

Explanation

Why use clickify.js?

Hashbang'd links <a href='http://<site>/#!/whatever'>8====&gt;</a> suck. They're ugly, bad for search engines and worse, you end up with a real URL and a hashbang'd URL that both reference the same content on the server.

clickify.js simplifies your single-page web application by allowing non-hash'd URLs with Backbone Router. Use normal links throughout your application, clickify.js will intercept them for use in your single-page app.

This leads to a seamless single-page app experience for users with modern browsers, and standard links for those without (and bots). Best of all, you don't have two links for each piece of content (good for bookmarks, sharing, pagerank, etc)

For nodejs/expressjs users, clickify.js allows you to match your backbone and express routes. Perfect if you plan on rendering the initial page request server-side and all subsequent requests via ajax.

How does it work?

clickify.js creates an event handler on all internal (relative url or same root-url) links. Apply it to any jQuery element, or the entire document body. Remember to apply clickify to any new elements that get inserted to the DOM.

Add class="no-click" to any anchor tags clickify should skip.

How do I use it?

$(function() { // DOM ready
  // Create a router to handle our URL changes
  var SimpleRouter = Backbone.Router.extend({

    routes: {
      'posts/:id': 'post',
      '*notFound': 'notFound' // catch-all for undefined routes
    },

    post: function(id) {
      ...
    },

    notFound: function() {
      ...
    }

  });
  
  new SimpleRouter;
  
  Backbone.history.start({ pushState: true }); // Start tracking history
  
  $(document.body).clickify(); // Add click handlers to all internal links
});
(function($){
// Declare the rootUrl used for filtering internal links.
var rootUrl = document.location.protocol + '//' + (document.location.hostname || document.location.host) + (document.location.port ? ':' + document.location.port : '') + '/';
// Helper functions
var getFragment = function(url, root) { // Grab the fragment and format it how Backbone expects
var fragment = url;
if (fragment.indexOf(':') !== -1)
fragment = fragment.replace(/.*:\/\/[^\/]+/, '');
if (!fragment.indexOf(root)) fragment = fragment.substr(root.length);
return fragment.replace(/^[#\/]/, '');
}
// jQuery selector for tagging internal links
$.expr[':'].internal = function(obj, index, meta, stack) {
var url = $(obj).attr('href') || '';
return (url.substring(0, rootUrl.length) === rootUrl || url.indexOf(':') === -1); // same domain || relative link
};
$.fn.clickify = function(options) {
options = $.extend({ 'root': '/' }, options); // Set the root option if your single-page app isn't in the site root
var anchorTags = this.find('a:internal:not(.no-click)');
anchorTags.click(function (event) {
var $this = $(this),
url = $this.attr('href');
if (event.which == 2 || event.metaKey)
return true;
// Make the call to Backbone.History
// Backbone will call pushState, update the hash or change window.location depending on what our browser supports
// Regardless, the part you should care about is that your routes are being called.
Backbone.history.navigate(getFragment(url), { trigger: true });
event.preventDefault();
return false;
});
anchorTags.addClass('no-click'); // Mark tags we've already added our event handler to
return this; // chainability
};
})(jQuery);
@gavinengel
Copy link

Is there a way to accomplish this without backbone, and only with jQuery? Or better yet, without needing jQuery at all?

@jlcj85
Copy link

jlcj85 commented May 24, 2014

How about reloading page? I get 404 error when I reloading page whith the posts/:id link.

@ashishsajwan
Copy link

if there is any demo ..that wud be great !! .. before i actually go n use this...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment