Using jQuery, Plugins and UI Controls With Backbone

Most Backbone applications either use jQuery or Zepto as their DOM manipulation of choice. I tend to use jQuery as it’s supported across more browsers and has more features – though it is a little heavier in terms of download size (and maybe performance). I also use a lot of jQuery plugins for various controls, to create specific effects, etc. It’s generally easy to do, as Backbone’s views provide direct access to a jQuery element as “this.el” or “this.$el”. From there, we can call standard jQuery code and plugins.

There are some common patterns I’ve noticed for doing this, too. Specifically, when to call a particular jQuery function or plugin largely depends on the purpose of that function and sometimes depends on how that function or plugin is built.

DOM Dependent/Independent

At a very basic level, jQuery manipulations of the DOM can fall in to 1 of 2 categories: DOM-dependent and DOM-independent. Many of the operations that we do in jQuery are dependent on the HTML that we’re manipulating being in the DOM already. Some of them don’t need the HTML to be part of the DOM, though. In those cases, we can work with document fragments that aren’t currently displayed or part of the DOM.

For example, it doesn’t make much sense to call “.slideUp” if the HTML we’re manipulating isn’t currently displayed. The animation that this method causes would still run, but we wouldn’t see anything. The end result would not be visible and we would have wasted the browser’s execution time on this animation. I generally lump visual changes and animations in to the DOM-dependent category because it doesn’t make sense to use these methods if the HTML is not in the DOM yet.

On the other hand, I often call “.hide” on document fragments before they are attached to the DOM. I do this so that when the HTML fragments are finally added to the DOM, they won’t be visible to the user. Then, after I’ve attached the HTML to the DOM, I can call “slideUp” or any of a number of other methods in jQuery, to cause the content to be displayed. I call these methods DOM-independent because they can be called whether we are working with a document fragment that only exists in memory, or working with an element that is already part of the DOM.

Of course, this is a great oversimplification of things. There is a lot of functionality in jQuery that doesn’t even deal with HTML elements or the DOM at all. These would clearly fall in to the DOM-independent category, but I’m not even going to address those right now. And even though this is a simplified way to view jQuery’s functions, it’s generally useful. It also helps us understand when we should call some of these methods when we think about integration with Backbone.

Simple Manipulations And Events

Most of the simple manipulations that we perform with jQuery are DOM-independent. We can call them and manipulate a document fragment or a DOM element directly, whenever we want to. This includes not only showing / hiding HTML elements, but also adding and removing them, attaching events, and more.

Consider this example:

Backbone.View.extend({
  // this is the default for backbone views
  // tagName: "div" 

  render: function(){
    var html = $("ul");

    html.append("<li>foo</li>");
    html.hide();

    this.$el.html(html);
  }
});

Here we can see a number of jQuery manipulations and events in place.

Starting with the core of Backbone’s View, an HTML element is generated when a view instance is created. This gets populated into “this.el” and cached in a jQuery selector object as “this.$el”.

In the render view, then, we see that we are using jQuery to create additional document fragments. For performance reasons (since it’s possible that the view’s “el” was attached to the DOM without the view knowing it), we’re populating the document fragment with content entirely in memory. This includes the addition of some data and calling “hide” on the fragment. We can manipulate the fragment with DOM-independent functions as much as we want, at this point.

Once we’ve completed the rendering of the HTML contents, we stuff it all in to the view’s el. The el is then attached to the DOM and the content we generated and we can begin using DOM-dependent methods.

We can also attach DOM level events to the fragments that we’ve generated.  Since jQuery is turning our strings in to proper document fragments for us, the DOM events are available. That means we can call methods like “.click” and “.blur” on the view’s “el” if we want to. But I generally consider this to be an anti-pattern. There are some cases where you’ll need to manually attach events, and it can be done here in the render method.  For the most part, though, we should be using Backbone’s declarative “events” on our views.

DOM Events And Simple Animations

Backbone’s “View” object abstracts a little bit of jQuery’s event system for us, through the use of the declarative “events”.

Backbone.View.extend({
  events: {
    "click": "showHide"
  },

  showHide: function(){
    this.$("ul").slideToggle();
  },

  // render: function ...
});

Surprisingly, DOM events are partially DOM-independent. As I said above, we can add the events to the document fragments before they become part of the DOM. We can even trigger them manually without them being part of the DOM. In general, though, we let the DOM fire it’s events for us.

Allowing the user to fire DOM events through DOM interactions (clicks, blurs, changes, etc) is DOM-dependent, of course. The view must attach any HTML structure that it needs to the DOM before the user can interact with them, to fire these events.

Often when we “click” or “change” or fire off some other DOM level event in our code, we want to respond to this by manipulating the DOM in a visual manner. For example, we might want to call “.hide(‘slow’)” on a portion of the view’s “el” in order to hide some items on the screen with a simple animation. The above example shows this. When you click on the top level “div”, the child “ul” is shown or hidden using the “slideToggle” animation method from jQuery.

jQuery Plugins And UI Controls

There are a large number of jQuery UI controls available, including the jQueryUI suite itself. I’ve used this suite and many other plugins for UI controls with Backbone a lot.

Generally speaking, most jQuery UI controls are partially DOM-independent in that you can call the jQuery plugin method to get them started before the document fragment you’re working with is attached to the DOM. Once the document fragment has been configured with the plugin’s code and additional structure, though, the plugins generally become DOM-dependent (as illustrated in the previous section).

For example, if you want to convert a “ul” list in to a menu structure using KendoUI, you can call “.kendoMenu” during the render method of the view:

var MyView = Backbone.View.extend({
  tagName: "ul",

  render: function(){
    this.$el.html("<li>foo</li><li>bar<ul><li>baz</li><li>quux</li></ul></li>");
    this.$el.kendoMenu();
  }
});

// ... somewhere else in the app
var view = new MyView();
view.render();
$("#someDiv").html(view.el);

Once the Kendo menu in configured, though, the view’s “el” has to be attached to the DOM (if it isn’t already). When that happens, you’ll see the menu structure in place.

DOM-Dependent UI Controls

I have run in to a few scenarios where a jQuery plugin was entirely DOM-dependent – or at least, my usage of it was. This meant that I could not call the plugin’s method prior to the Backbone view’s “el” being added to the DOM.

The “easy” solution to this is to have the view attach it’s “el” directly to the DOM in some fashion. But as I’ve talked about before, this is a bad idea. Instead, it only takes a few extra lines of code – which can be easily extracted in to something reusable – to make this work.

var AnotherView = Backbone.View.extend({
  onShow: function(){
    this.$el.layout({ appDefaultStyles: true});
  }
});

In this example, I’ve added a method to the view called “onShow”. This method contains the call to the jQuery “Layout Manager” plugin, which in my experience has been entirely DOM-dependent. Note that the “onShow” method doesn’t get called from within the view, though. This is because it’s not the view’s responsibility for adding itself to the DOM. It’s the responsibility of the code that needs the view, to do this.

var view = new AnotherView();
view.render();
$("#someDiv").html(view.el);

if (view.onShow){
  view.onShow();
}

The code that needs the view will instantiate it, call render and attach the resulting “view.el” to the DOM somehow. This is pretty straight-forward so far. What is does next, though, is check for the existence of an “onShow” method in the view. If that method does exist, it gets called. Since the code that is using the view knows that it has already added the view to the DOM, it knows when to call the “onShow” method. This allows us to write code that relies on DOM-dependent functionality without the view having to know if it’s been added to the DOM or not.

(FWIW: in retrospect, I regret using the Layout Manager plugin that I linked to and showed. It’s caused me a lot of frustration. I find it far easier to use KendoUI’s “Splitter” control, instead.)

Extracting The onShow Method Call

I’ve written this “onShow” method and the code to call it, a countless number of times. It became such a ubiquitous part of my code that I added it to my “RegionManager” in Backbone.Marionette (a region manager is responsible for managing the rendering, display and closing of a view, for a given region of the screen).

I’ve also found it to be very useful to have two additional methods that are called on my views: onClose and onRender. I specifically have onClose and onRender. onClose is called by the region manager when closing an existing view. onRender, though, is called by my base “ItemView” or “CollectionView” in Marionette, as I’ve extracted the core rendering in to these two objects but still want to provide a way for specific views to take advantage of DOM-independent plugins and UI controls.

Context Is Still King

As always, the patterns and implementations that I’ve shown here are entirely contextual. You’ll likely find scenarios where some DOM-independent code should not be called until after the view’s “el” is part of the DOM, for example. Use your judgement, experience and trial-and-error with the various methods and functions that you have to call to get your behavior correct.

Full Disclosure On KendoUI

I want to be up front about my use of KendoUI in this post. I’m using KendoUI in some of these examples and I link to it directly because I love this control suite. I was, however, given a free Telerik Ultimate subscription so that I could continue using Kendo and the other tools. I was asked to talk about Kendo when appropriate, in return for this subscription. When Telerik offered that to me, I gladly accepted – not because I saved a few hundred $, but because I was going to write this anyways and spend my own $ on the control suite. It was simply a win-win situation for me, to get the free subscription and to help spread the word on a control suite that I love using with Backbone.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Backbone, DOM, HTML5, Javascript, JQuery, KendoUI, Marionette, Telerik, Tools and Vendors. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Erik

    Thanks for another great post Derick!  Can you tell me how you handle jQuery UI Dialogs with Backbone?  Specifically, what do you bind the view/element to?  I’ve experimented with it a bit, and can never seem to get event binding to work correctly…

    • Alex

      This is a big issue that I’ve hit up against as well.
      (and actually in the HackerNews thread I left the same comment because I believe the commenter there had the same type of issue: http://news.ycombinator.com/item?id=3616497)
      For example, trying to tie a Backbone View (el, events) to a Twitter Bootstrap “Popover”(http://twitter.github.com/bootstrap/javascript.html#popovers) doesn’t work in the “Backbone way”.
      Are talking about the same thing?

    • http://mutedsolutions.com Derick Bailey

      the problem that you and Alex are running in to is usually caused by the way the dialog boxes are handled, at the DOM level. 

      nearly every modal dialog system takes the DOM element that you hand it, removes it from the DOM and places it somewhere else – either in memory, or in a special holding area of the DOM that’s been hidden from the user.

      when this happens – when the view’s “el” is removed from the DOM, you lose all of your declared events on the view, and things don’t work anymore.

      the solution is not to call the dialog directly against the view. use an intermediate object (similar to the RegionManager I talk about) that represents the dialog, and then have it show the view’s content in that.

      var Dialog = {
        el: “#dialogDiv”,
        show: function(view){
          view.render();
          var $el = $(el);
          $el.html(view.el);    
          $el.dialog();
        }
      }

      by doing this, it’s the “#dialogDiv” that gets moved around in the DOM when you call the $.dialog method instead of the view’s “el”, so you don’t lose your view’s declared events.

  • Eric

    Derick,

    Love your stuff on Backbone. I’m looking at doing a mobile app with Backbone and jQuery Mobile or Kendo UI Mobile. Have you (or will you) posted any samples of Kendo and Backbone together?

    Also, any thoughts about Backbone vs Kendo’s MMVM support?

    Thanks,
    Eric

    • http://mutedsolutions.com Derick Bailey

      Mobile web is something I’ve been wanting to get into, but haven’t had a chance to do, yet. When I do, you can be sure that I’ll be talking about it. It’s just a question of when. :)

      This post is the closest thing I have to a Backbone + Kendo post, so far. I’m using the two together pretty regularly at this point, though. The ideas that I talk about in this post apply to Kendo pretty well, which is why I included Kendo in some of the examples here.

      I’ve only briefly read a little bit about Kendo’s MVVM… I’m not sure what to think, yet. I have a lot of reservations about things that I don’t know enough about yet. I’m “cautiously optimistic” would be the best answer at this point. :)

  • Mike

    Hi Derek,

    Marionette’s onShow() method doesn’t seem to be smart enough to cater for deeply nested views. I’m dealing with a situation, where the view that needs to do dom-dependent work is contained within a layout, contained within other views (including another layout).

    Attempting to do dom-dependent work in the view’s onShow runs into same problem as if I did it in the view’s onRender.

    Reviewing the call stack, I can see that the view’s onShow is being called from the parent layout’s onRender. This suggests that onShow doesn’t necessarily guarantee the view is in the dom if the parent layout is not at the root of the view tree.

    Is that right, or am I doing something bonkers? Is there a way I can acheive what I need within marionette without having to implement my own “really-on-show” mechanism for deep nesting?

    Appreciate your work with Marionette – so much better than working with Backbone alone! Cheers.

    • Mike

      Ok, I’ve worked out what the issue is.

      On first read, this article (and the marionette doco) seems to imply that onShow() is the sliver bullet callback that tells you when your view has been inserted into the DOM.

      Unfortunately this is only true if the parent view is in the DOM at the time it calls region.show(childview). To be fair, after re-read armed with what I now know, Derick does hint at this in the section “Context Is Still King”.

      The problem manifests when the parent isn’t in the dom, as would be the case when rendering multiply nested views, and as this fiddle illustrates:

      http://jsfiddle.net/h8436/7/

      The only solution I can come up with is to manually have whatever the root view is, trigger a custom ‘now-in-dom’ event on its children, that each in turn propegates to each of their children, eventually reaching the final child view so that it can go do it’s DOM-dependent thing.

      However this still doesn’t work for arbitrary levels of nesting. If a re-render occured not on the root node, but at say 2 levels down with the child view at another 3 levels down, a separate and different event would have to be triggered by the re-rendered view, and to which the child view would have to also listen for.

      I can’t (yet) see a way for this to happen with arbitrary nesting levels and views being agnostic to the requirements of others higher up or lower down the chain. Any ideas?

      • http://mutedsolutions.com Derick Bailey

        Hi Mike,

        The combination of the “show” event/method and “DOMRefresh” event/method were supposed to be the “silver bullet” for this… I probably broke something when doing some of the code cleanup that I’ve done in the last few releases.

        What version of Marionette are you using? I’ll copy all of this text in to an issue on the Github project page and dig in to it as soon as I can. I’m travelling today, so I won’t be able to get to this right away, though.

        Thanks for digging in to this further and for building that fiddle!

        • Henry

          Hi Derick,

          It’s been 3 months since you last replied, is there any update on this? What’s the Github issue #?

          Thanks,
          Henry

  • Uchiha Itachi

    Can any .NET UI components work with jQuery:http://www.kettic.com/