How do I add a resize event to the window in a view using Backbone?

16,965

Solution 1

var BaseView = Backbone.View.extend({

    el: $('body'),

    initialize: function() {
        // bind to the namespaced (for easier unbinding) event
        // in jQuery 1.7+ use .on(...)
        $(window).bind("resize.app", _.bind(this.resize, this));
    },

    remove: function() {
        // unbind the namespaced event (to prevent accidentally unbinding some
        // other resize events from other code in your app
        // in jQuery 1.7+ use .off(...)
        $(window).unbind("resize.app");

        // don't forget to call the original remove() function
        Backbone.View.prototype.remove.call(this);
        // could also be written as:
        // this.constructor.__super__.remove.call(this);
    }, ...

Don't forget to call the remove() function on the view. Never just replace the view with another one.

Solution 2

You might let window.onresize trigger a custom event and then let Views or Models listen to that to have custom responses for various elements.

Case 1. A view listens to the window event directly.

window.onload = function() {

  _.extend(window, Backbone.Events);
  window.onresize = function() { window.trigger('resize') };

  ViewDirect = Backbone.View.extend({

    initialize: function() {
      this.listenTo(window, 'resize', _.debounce(this.print));
    },

    print: function() {
      console.log('Window width, heigth: %s, %s',
        window.innerWidth,
        window.innerHeight);
    },

  });

  var myview = new ViewDirect();

  }

Case 2. You may want to retain the window size without inspecting it each time you need it, hence you store the window size in a backbone model: in this case the window model listens to the window, while the view listens to the window model:

window.onload = function() {

  _.extend(window, Backbone.Events);
  window.onresize = function() { window.trigger('resize') };

  WindowModel = Backbone.Model.extend({

    initialize: function() {
      this.set_size();
      this.listenTo(window, 'resize', _.debounce(this.set_size));
    },

    set_size: function() {
      this.set({
        width: window.innerWidth,
        height: window.innerHeight
      });
    }

  });

  ViewWithModel = Backbone.View.extend({

    initialize: function() {
      this.listenTo(this.model, 'change', this.print);
      ...
    },

    print: function() {
      console.log('Window width, heigth: %s, %s',
        this.model.width,
        this.model.height);
    },
  });

  var window_model = new WindowModel();
  var myview = new ViewWithModel({model: window_model});

}
Share:
16,965
Eli
Author by

Eli

Updated on June 03, 2022

Comments

  • Eli
    Eli almost 2 years

    I have been trying to attach a handler to the resize event in one of my Backbone views. After doing some research I have discovered that you can only attach events to the view's element or its descendants.

    This is an issue for me because the visual effect I am trying to achieve is not possible using pure CSS and requires some JS to set the dimensions of the content area element based on the window minus the header element.

    If you are having trouble visualizing what I am trying to do, imagine a thin header and a content area which must occupy the remaining space with no CSS background trickery.

    define(
        [
            'jQuery',
            'Underscore',
            'Backbone',
            'Mustache',
            'text!src/common/resource/html/base.html'
        ],
        function ($, _, Backbone, Mustache, baseTemplate) {
            var BaseView = Backbone.View.extend({
    
                el: $('body'),
    
                events: {
                    'resize window': 'resize'
                },
    
                render: function () {
                    var data = {};
    
                    var render = Mustache.render(baseTemplate, data);
    
                    this.$el.html(render);
    
                    this.resize();
                },
    
                resize: function () {
                    var windowHeight = $(window).height();
    
                    var headerHeight = this.$el.find('#header').height();
    
                    this.$el.find('#application').height( windowHeight - headerHeight );
                }
            });
    
            return new BaseView;
        }
    );
    
  • Eli
    Eli over 12 years
    Thankyou for your quick reply! But is it bad practice to go outside of Backbone's event layer?
  • wheresrhys
    wheresrhys over 12 years
    I would say don't worry about it too much. The resize event doesn't do any complex interaction with your MVC structure so you're not really losing anything by just having a separate listener
  • Eli
    Eli over 12 years
    Thankyou for clarifying that for me
  • mu is too short
    mu is too short over 12 years
    You might want a remove method in the view that unbinds that event, you'll get stray bindings and leaks if the view is removed without shutting down the whole page.
  • jantimon
    jantimon about 11 years
    Nice answer however your remove would unbind all resize.app events which might be a problem if you use this view more than once.
  • talltolerablehuman
    talltolerablehuman about 11 years
    @jantimon - the example fits the question, as it was meant for the base view with ´el: $('body')´. There could only be one. But of course, if you want to use this in other views you should add a view-id to the event namespace.
  • sam1132
    sam1132 over 10 years
    is there a reason the model is involved in this event process? I would normally think of resize as a view-layer event.
  • prodaea
    prodaea over 10 years
    @mlibby it depends on what's changing. It is possible that the event would change the model so that the view only has to concern itself with events from the model.
  • mindrones
    mindrones over 10 years
    Hi @mlibby sorry for the late replay, I didn't notice your question. Edited to answer, also to remark what prodaea said above.