Create a gist now

Instantly share code, notes, and snippets.

A really simple Javascript router
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Building a router</title>
<script>
// Put John's template engine code here...
(function () {
// A hash to store our routes:
var routes = {};
// An array of the current route's events:
var events = [];
// The element where the routes are rendered:
var el = null;
// Context functions shared between all controllers:
var ctx = {
on: function (selector, evt, handler) {
events.push([selector, evt, handler]);
},
refresh: function (listeners) {
listeners.forEach(function (fn) { fn(); });
}
};
// Defines a route:
function route (path, templateId, controller) {
if (typeof templateId === 'function') {
controller = templateId;
templateId = null;
}
var listeners = [];
Object.defineProperty(controller.prototype, '$on', {value: ctx.on});
Object.defineProperty(controller.prototype, '$refresh', {value: ctx.refresh.bind(undefined, listeners)});
routes[path] = {templateId: templateId, controller: controller, onRefresh: listeners.push.bind(listeners)};
}
function forEachEventElement(fnName) {
for (var i = 0, len = events.length; i < len; i++) {
var els = el.querySelectorAll(events[i][0]);
for (var j = 0, elsLen = els.length; j < elsLen; j++) {
els[j][fnName].apply(els[j], events[i].slice(1));
}
}
}
function addEventListeners() {
forEachEventElement('addEventListener');
}
function removeEventListeners() {
forEachEventElement('removeEventListener');
}
function router () {
// Lazy load view element:
el = el || document.getElementById('view');
// Remove current event listeners:
removeEventListeners();
// Clear events, to prepare for next render:
events = [];
// Current route url (getting rid of '#' in hash as well):
var url = location.hash.slice(1) || '/';
// Get route by url or fallback if it does not exist:
var route = routes[url] || routes['*'];
// Do we have a controller:
if (route && route.controller) {
var ctrl = new route.controller();
if (!el || !route.templateId) {
// If there's nothing to render, abort:
return;
}
// Listen on route refreshes:
route.onRefresh(function () {
removeEventListeners();
// Render route template with John Resig's template engine:
el.innerHTML = tmpl(route.templateId, ctrl);
addEventListeners();
});
// Trigger the first refresh:
ctrl.$refresh();
}
}
// Listen on hash change:
this.addEventListener('hashchange', router);
// Listen on page load:
this.addEventListener('load', router);
// Expose the route register function:
this.route = route;
})();
</script>
<script type="text/html" id="home">
<h1>Router FTW!</h1>
</script>
<script type="text/html" id="template1">
<h1>Page 1: <%= greeting %></h1>
<p><%= moreText %></p>
<button class="my-button">Click me <%= counter %></button>
</script>
<script type="text/html" id="template2">
<h1>Page 2: <%= heading %></h1>
<p>Lorem ipsum...</p>
</script>
<script type="text/html" id="error404">
<h1>404 Not found</h1>
</script>
</head>
<body>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#/page1">Page 1</a></li>
<li><a href="#/page2">Page 2</a></li>
</ul>
<div id="view"></div>
<script>
route('/', 'home', function () {});
route('/page1', 'template1', function () {
this.greeting = 'Hello world!';
this.moreText = 'Bacon ipsum...';
this.counter = 0;
this.$on('.my-button', 'click', function () {
this.counter += 1;
this.$refresh();
}.bind(this));
});
route('/page2', 'template2', function () {
this.heading = 'I\'m page two!';
});
route('*', 'error404', function () {});
</script>
</body>
</html>
@thiloilg
thiloilg commented Nov 5, 2015

thank you!!! very helpful!!!

@rlynjb
rlynjb commented Mar 1, 2016

hello. i was wondering if there is a way to reload a current template and its controller?

@joakimbeng
Owner

@rlynjb I've updated the gist to include event handling and refreshing of routes. I've removed the Object.observe as well because it's already deprecated.

You register event listeners with the $on function in a controller, and you can trigger a refresh/rerender with the $refresh function. See the /page1 route in the example code above.

I still don't consider this a production ready solution though. For a more complete but still small router library have a look at: page

@dannycallaghan

@Shyam-Chen

Put John's template engine code where it says:

// Put John's template engine code here...

The link to the code is in the first comment.

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