Skip to content

Instantly share code, notes, and snippets.

@jbroadway
Created June 27, 2012 18:05
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save jbroadway/3005741 to your computer and use it in GitHub Desktop.
Save jbroadway/3005741 to your computer and use it in GitHub Desktop.
Simple History.js-based client-side router
var app = (function ($) {
var self = {};
// change pages via app.go('/new/page') or app.go(-1)
self.go = function (url) {
if (url === parseInt (url)) {
History.go (url);
} else {
History.pushState (null, null, url);
}
};
// handle click events
self.click_handler = function (e) {
var url = $(this).attr ('href'),
title = $(this).attr ('title');
if (url.match (/:\/\//)) {
return true;
}
if (url === '#') {
return false;
}
e.preventDefault ();
History.pushState ({}, title, url);
};
// show home
self.show_home = function () {
alert ('Home!');
};
// show item
self.show_item = function (id) {
alert ('Item ' + id);
};
return self;
})(jQuery);
<script src="history.js"></script>
<script src="router.js"></script>
<script src="app.js"></script>
<script src="init.js"></script>
<p><a href="/">Home</a> | <a href="/item/123">Item 123</a> | <a href="/item/456">Item 456</a></p>
(function (window, undefined) {
var History = window.History;
if (! History.enabled) {
return false;
}
router.set_routes ({
'/': app.show_home,
'/item/:id': app.show_item
// ...etc...
});
History.Adapter.bind (window, 'statechange', function () {
var State = History.getState ();
router.route (State.url);
});
// catch all link clicks
$('body').on ('click', 'a', app.click_handler);
// now use app.go(url) to move around
router.route (router.get_path (window.location.href));
})(window);
/**
* A simple router class to be used in conjunction with History.js.
* Triggers the appropriate callback function when `router.route(url)`
* is called.
*
* Usage:
*
* <script src="history.js"></script>
* <script src="router.js"></script>
* <script>
* (function (window, undefined) {
* var History = window.History;
* if (! History.enabled) {
* return false;
* }
*
* function handler_one () {
* console.log ('handler_one() called!');
* }
*
* function handler_two (name) {
* console.log ('handler_two() called!');
* console.log (name);
* }
*
* router.set_routes ({
* '/one': handler_one,
* '/hello/:name': handler_two
* });
*
* History.Adapter.bind (window, 'statechange', function () {
* var State = History.getState ();
* router.route (State.url);
* });
*
* History.pushState (null, null, '/one');
* History.pushState (null, null, '/hello/world');
* })(window);
* </script>
*/
var router = (function () {
var router = {};
// list of routes
router.routes = {};
// create once, used by get_path()
router.a = document.createElement ('a');
// turn a url into a regex and params
router.regexify = function (url) {
var res = {
url: url,
regex: null,
params: []
};
// parse for params
var matches = url.match (/\:([a-zA-Z0-9_]+)/g);
if (matches !== null) {
for (var i in matches) {
matches[i] = matches[i].substring (1);
}
res.params = matches;
url = url.replace (/\:([a-zA-Z0-9_]+)/g, '(.*?)');
}
res.regex = url.replace ('/', '\\/');
return res;
};
// set a list of routes and their callbacks
router.set_routes = function (routes) {
for (var url in routes) {
res = router.regexify (url);
var r = {
url: url,
regex: new RegExp ('^' + res.regex + '/?$', 'g'),
params: res.params,
callback: routes[url]
};
router.routes[url] = r;
}
};
// get the relative path from a full url
router.get_path = function (url) {
router.a.href = url;
return router.a.pathname + router.a.search + router.a.hash;
};
// handle the routing for a url
router.route = function (url) {
var path = router.get_path (url);
for (var i in router.routes) {
var matches = router.routes[i].regex.exec (path);
router.routes[i].regex.lastIndex = 0;
if (matches !== null) {
if (matches.length > 1) {
matches.shift ();
router.routes[i].callback.apply (null, matches);
} else {
router.routes[i].callback ();
}
break;
}
}
};
return router;
})();
@noxecane
Copy link

I don't know who you are but you saved my life

@nerdess
Copy link

nerdess commented Jun 24, 2016

thanks for this code, exactly what i am looking for :)

@timothyallan
Copy link

Note that you need to do something like to get it working with IE11!

  getPath(url) {
    this.a.href = url;
    
    // IE11 returns path name without a leading /, which the regexes need
    const pathName = this.a.pathname.indexOf('/') !== 0 ? `/${this.a.pathname}` : this.a.pathname;
    return `${pathName}${this.a.search}${this.a.hash}`;
  }

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