Search code examples
javascriptjquerydombackbone.jsmarionette

marionette.js vs Backbone updating DOM


I have noticed there is a different, the way DOM been updated by Marionette comparing with Backbone. I have created two simple fiddles. One using Backbone and other one based on marionette. Both examples has a helper method call processDom and that method simply throw an error after iterating 50 times.

However in backbone example elements been appended to DOM till die() method fires. But in marionette based example DOM has not been updated at all. It would be great if someone can explain how this works. I wonder marionette internally using a virtual dom kind of a technique.

Render method in marionette example

render: function () {
            var viewHtml = this.template({
                'contentPlacement': 'Sydney'
            });
            this.$el.html(viewHtml);
            this.processDom(this.$el);
            this.postRender();
        }

Render method in backbone example

render: function () {
        var template = Handlebars.compile($('#sample-template').html());
        var viewHtml = template({
                'contentPlacement': 'Sydney'
            });
        this.$el.html(viewHtml);
        this.processDom(this.$el);
        this.postRender();
    },

Links to fiddle examples

http://jsfiddle.net/samitha/v5L7c2t5/7/

http://jsfiddle.net/samitha/pc2cvmLs/7/


Solution

  • In general for post processing purposes you could use onRender of Marionette.ItemView. It's not a good practice to rewrite Marionette.ItemView.render.

    Rendering of views inside the regions for Marionette is handled a bit different as in Backbone case.

    When you rendering Backbone.View - your element will attach itself to the DOM's $('#search-container'), and in render method it will operate with already attached element so you can see the changes.

    When you rendering Marionette.ItemView with Marionette.Region.show method, Marionette.Region already attached to the DOM and it need to render appropriate view (in your case ItemView) and only after that step it will attach it to the DOM and will set the ItemView as currentView.

    You can see from source of Marionette.Region.show that it attaches view after render is called. In your case render will throw error and it will never be attached to the DOM.


    To understand it deeper lets look at the 2 methods which are responsible for attaching/creating views in BackboneView. Marionette uses the same method for Marionette.ItemView.

    _ensureElement: function() {
      if (!this.el) { // case when el property not specified in view
        var attrs = _.extend({}, _.result(this, 'attributes'));
        if (this.id) attrs.id = _.result(this, 'id');
        if (this.className) attrs['class'] = _.result(this, 'className');
        // creating new jQuery element(tagNam is div by default) with attributes specified
        var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
        this.setElement($el, false);
      } else {  // case when el property exists
        this.setElement(_.result(this, 'el'), false);
      }
    }
    

    And

    setElement: function(element, delegate) {
      if (this.$el) this.undelegateEvents();
      // here is all magic
      // if element is instance of Backbone.$, which means that el property not specified
      //  or it's already jquery element like in your example( el: $('#search_container')) with Backbone. 
      // this.$el will be just a jQuery element or already attached to the DOM jQuery element
      // in other case it will try to attach it to the DOM.
      this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
      this.el = this.$el[0];
      if (delegate !== false) this.delegateEvents();
      return this;
    },
    

    As you can see from comments all magic is in a few lines of code, and that's why I love it.