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.