Responsive template swapping with Backbone
CSS media queries sometimes aren't quite enough to get a design "just right" for every screen-size, especially for web-apps where you might want to change the UI more drastically than more content-centric blogs or product-pages.
For Backbone applications, here's an example using window.matchMedia
to swap which template to render depending on whether a given media query matches.
See the Pen Responsive template swapping with Backbone by Chang Wang (@cheapsteak) on CodePen
If you're not already familiar with matchMedia
, I would suggest taking a look at MDN's examples that do a great job of showing how it's used.
My initial iteration of the view was a bit less complex:
var ResponsiveView = Backbone.View.extend({
initialize: function (options) {
var self = this;
if (window.matchMedia) {
var mqlListener = function (mql) {
if (mql.matches) {
self.template = options.matchedTemplate;
} else {
self.template = options.unmatchedTemplate;
}
self.render();
};
var mql = window.matchMedia("("+options.breakpoint+")");
mql.addListener(mqlListener);
mqlListener(mql);
} else {
self.template = matchedTemplate; //IE9 and older
}
},
render: function () {
var html = this.template({repos: repos});
this.$el.html(html);
}
});
A MediaQueryList mql
and a MediaQueryListListener mqlListener
are created in the view's constructor. Whenever the test result from mql's query breakpoint
changes, mqlListener is called and sets the appropriate template for the view and calls render
to redraw everything. (By the way, you can omit the check for window.matchMedia
if you already have it polyfilled for IE9 and below)
This has a problem though - Zombie Views. This is because mqlListener
holds a reference to the view, and since mqlListener
isn't ever removed, the view will never be garbage-collected.
To solve that, we need to have a handle on both mql
and mqlListener
so we can remove the listener when the view's remove
method is called, and since we have to overwrite the original remove
to do that, Backbone.View.prototype.remove.call(this)
is required as a way of calling remove
's super function.
I'm still getting into Jekyll and haven't implemented commenting yet. If you have an improvement or see any pitfalls with this method, please shoot me a tweet.