You are previewing JavaScript Web Applications.

JavaScript Web Applications

Cover of JavaScript Web Applications by Alex MacCaw Published by O'Reilly Media, Inc.
  1. JavaScript Web Applications
    1. SPECIAL OFFER: Upgrade this ebook with O’Reilly
    2. Preface
      1. Who Is This Book For?
      2. How This Book Is Organized
      3. Conventions Used in This Book
      4. Accompanying Files
      5. Code Conventions
      6. Holla
      7. Author’s Note
      8. Safari® Books Online
      9. How to Contact Us
    3. 1. MVC and Classes
      1. Early Days
      2. Adding Structure
      3. What Is MVC?
      4. Toward Modularity, Creating Classes
      5. Adding Functions to Classes
      6. Adding Methods to Our Class Library
      7. Class Inheritance Using Prototype
      8. Adding Inheritance to Our Class Library
      9. Function Invocation
      10. Controlling Scope in Our Class Library
      11. Adding Private Functions
      12. Class Libraries
    4. 2. Events and Observing
      1. Listening to Events
      2. Event Ordering
      3. Canceling Events
      4. The Event Object
      5. Event Libraries
      6. Context Change
      7. Delegating Events
      8. Custom Events
      9. Custom Events and jQuery Plug-Ins
      10. Non-DOM Events
    5. 3. Models and Data
      1. MVC and Namespacing
      2. Building an ORM
      3. Adding ID Support
      4. Addressing References
      5. Loading in Data
      6. Populating Our ORM
      7. Storing Data Locally
      8. Adding Local Storage to Our ORM
      9. Submitting New Records to the Server
    6. 4. Controllers and State
      1. Module Pattern
      2. Adding a Bit of Context
      3. State Machines
      4. Routing
    7. 5. Views and Templating
      1. Dynamically Rendering Views
      2. Templates
      3. Binding
    8. 6. Dependency Management
      1. CommonJS
      2. Module Loaders
      3. Wrapping Up Modules
      4. Module Alternatives
      5. FUBCs
    9. 7. Working with Files
      1. Browser Support
      2. Getting Information About Files
      3. File Inputs
      4. Drag and Drop
      5. Copy and Paste
      6. Reading Files
      7. Custom Browse Buttons
      8. Uploading Files
      9. jQuery Drag and Drop Uploader
    10. 8. The Real-Time Web
      1. Real Time’s History
      2. WebSockets
      3. Real-Time Architecture
      4. Perceived Speed
    11. 9. Testing and Debugging
      1. Unit Testing
      2. Drivers
      3. Headless Testing
      4. Distributed Testing
      5. Providing Support
      6. Inspectors
      7. The Console
      8. Using the Debugger
      9. Analyzing Network Requests
      10. Profile and Timing
    12. 10. Deploying
      1. Performance
      2. Caching
      3. Minification
      4. Gzip Compression
      5. Using a CDN
      6. Auditors
      7. Resources
    13. 11. The Spine Library
      1. Setup
      2. Classes
      3. Events
      4. Models
      5. Controllers
      6. Building a Contacts Manager
    14. 12. The Backbone Library
      1. Models
      2. Collections
      4. Controllers
      5. Syncing with the Server
      6. Building a To-Do List
    15. 13. The JavascriptMVC Library
      1. Setup
      2. Classes
      3. Model
      4. Using Client-Side Templates in the View
      5. $.Controller: The jQuery Plug-in Factory
      6. Putting It All Together: An Abstract CRUD List
    16. A. jQuery Primer
      1. DOM Traversal
      2. DOM Manipulation
      3. Events
      4. Ajax
      5. Being a Good Citizen
      6. Extensions
      7. Creating a Growl jQuery Plug-in
    17. B. CSS Extensions
      1. Variables
      2. Mixins
      3. Nested Rules
      4. Including Other Stylesheets
      5. Colors
      6. How Do I Use Less?
    18. C. CSS3 Reference
      1. Prefixes
      2. Colors
      3. Rounded Corners
      4. Drop Shadows
      5. Text Shadow
      6. Gradients
      7. Multiple Backgrounds
      8. Selectors
      9. Transitions
      10. Border Images
      11. Box Sizing
      12. Transformations
      13. Flexible Box Model
      14. Fonts
      15. Graceful Degradation
      16. Creating a Layout
    19. Index
    20. About the Author
    21. Colophon
    22. SPECIAL OFFER: Upgrade this ebook with O’Reilly

Adding a Bit of Context

Using a local context is a useful way of structuring modules, especially when it comes to registering callbacks to events. As it stands, the context inside our module is global—this is equal to window:

  assertEqual( this, window );

If we want to scope the context, we need to start adding functions onto an object. For example:

  var mod = {};

  mod.contextFunction = function(){
    assertEqual( this, mod );


The context inside contextFunction() is now local to our mod object. We can start using this without worrying about creating global variables. To give you a better indication of how it would be used in practice, let’s further flesh out that example:


  var mod = {};

  mod.load = function(func){
    $($.proxy(func, this));

    this.view = $("#view");

  mod.assetsClick = function(e){
    // Process click

      $.proxy(this.assetsClick, this)


We’re creating a load() function that takes a callback, executing it when the page has loaded. Notice that we’re using jQuery.proxy() to ensure that the callback is invoked in the correct context.

Then, when the page loads, we’re adding a click handler onto an element, giving it a local function, assetsClick(), as a callback. Creating a controller doesn’t need to be any more complicated than that. What’s important is that all of the controller’s state is kept local and encapsulated cleanly into a module.

Abstracting into a Library

Let’s abstract that library out so we can reuse it with other modules and controllers. We’ll include the existing load() function and add new ones like proxy() and include():

(function($, exports){
  var mod = function(includes){
    if (includes) this.include(includes);
  mod.fn = mod.prototype;

  mod.fn.proxy = function(func){
    return $.proxy(func, this);

  mod.fn.load = function(func){

  mod.fn.include = function(ob){
    $.extend(this, ob);

  exports.Controller = mod;
})(jQuery, window);

proxy() ensures that functions are executed in the local context, which is a useful pattern for event callbacks. The include() function is just a shortcut for adding properties onto the controller, saving some typing.

We’re adding our library to the exports object, exposing it as the global Controller variable. Inside the module we can instantiate a Controller object using its constructor function. Let’s go through a simple example that toggles an element’s class depending on whether the mouse is over the element:

(function($, Controller){

  var mod = new Controller;

  mod.toggleClass = function(e){ 

    this.view = $("#view");
    this.view.mouseover(this.proxy(this.toggleClass), true);
    this.view.mouseout(this.proxy(this.toggleClass), false);

})(jQuery, Controller);

When the page loads, we’re creating a view variable and attaching some event listeners. They in turn call toggleClass() when the mouse moves over the element, toggling the element’s class. You can see the full example in this book’s accompanying files, in assets/ch04/modules.html.

Granted, using context rather than local variables means there is probably more code to write, what with all the usage of this. However, the technique gives us much greater scope for reusing code and including mixins. For example, we could add a function onto every Controller instance by setting a property on its prototype:

Controller.fn.unload = function(func){
  jQuery(window).bind("unload", this.proxy(func));

Or, we could extend an individual controller by using the include() function we defined earlier, passing it an object:

var mod = new Controller;

The StateMachine object, in this example, could be reused over and over again with our other modules, preventing us from duplicating code and keeping things DRY (don’t repeat yourself).

Loading Controllers After the Document

As it stands, some parts of our controllers are being loaded before the DOM, and other parts are in callbacks to be invoked after the page’s document has loaded. This can be confusing because the controller’s logic is being executed under different states, resulting in a lot of document load callbacks.

We can solve this in one fell swoop by loading controllers after the DOM. I personally advocate this approach because it ensures that you don’t need to think constantly about what state the page’s DOM is in when accessing elements.

Let’s first take advantage and clear up our library, making our controllers a bit cleaner. The Controller class doesn’t need to be a constructor function because the context switch needed when generating subcontrollers is unnecessary here:

// Use global context, rather than the window
// object, to create global variables
var exports = this;

  var mod = {};

  mod.create = function(includes){
    var result = function(){
      this.init.apply(this, arguments);

    result.fn = result.prototype;
    result.fn.init = function(){};

    result.proxy    = function(func){ return $.proxy(func, this); };
    result.fn.proxy = result.proxy;

    result.include = function(ob){ $.extend(this.fn, ob); }; 
    result.extend  = function(ob){ $.extend(this, ob); };
    if (includes) result.include(includes)

    return result;

  exports.Controller = mod;

Now we can use our new Controller.create() function to create controllers, passing in an object literal of instance properties. Notice that the entire controller is wrapped in jQuery(function(){ /* ... */ }). This is an alias for jQuery.ready(), and it ensures that the controller is loaded only after the page’s DOM has fully initialized:

  var ToggleView = Controller.create({
    init: function(view){
      this.view = $(view);
      this.view.mouseover(this.proxy(this.toggleClass), true);
      this.view.mouseout(this.proxy(this.toggleClass), false);          

    this.toggleClass: function(e){

  // Instantiate controller, calling init()
  new ToggleView("#view");

The other significant change we’ve made is passing in the view element to the controller upon instantiation, rather than hardcoding it inside. This is an important refinement because it means we can start reusing controllers with different elements, keeping code repetition to a minimum.

Accessing Views

A common pattern is to have one controller per view. That view has an ID, so it can be passed to controllers easily. Elements inside the view then use classes, rather than IDs, so they don’t conflict with elements in other views. This pattern provides a good structure for a general practice, but it should not be conformed to rigidly.

So far in this chapter we’ve been accessing views by using the jQuery() selector, storing a local reference to the view inside the controller. Subsequent searches for elements inside the view are then scoped by that view reference, speeding up their lookup:

// ...  
init: function(view){
  this.view = $(view);
  this.form = this.view.find("form");

However, it does mean that controllers fill up with a lot of selectors, requiring us to query the DOM constantly. We can clean this up somewhat by having one place in the controller where selectors are mapped to variables names, like so:

elements: {
  "form.searchForm": "searchForm",
  "form input[type=text]": "searchInput"

This ensures that the variables this.searchForm and this.searchInput will be created on the controller when it’s instantiated, set to their respective elements. These are normal jQuery objects, so we can manipulate them as usual, setting event handlers and fetching attributes.

Let’s implement support for that elements mapping inside our controllers, iterating over all the selectors and setting local variables. We’ll do this inside our init() function, which is called when our controller is instantiated:

var exports = this;

  exports.SearchView = Controller.create({
    // Map of selectors to local variable names
    elements: {
      "input[type=search]": "searchInput",
      "form": "searchForm"

    // Called upon instantiation
    init: function(element){
      this.el = $(element);

    search: function(){
      console.log("Searching:", this.searchInput.val());

    // Private

    $: function(selector){
      // An `el` property is required, and scopes the query
      return $(selector, this.el);

    // Set up the local variables
    refreshElements: function(){
      for (var key in this.elements) {
        this[this.elements[key]] = this.$(key);

  new SearchView("#users");

refreshElements() expects every controller to have a current element property, el, which will scope any selectors. Once refreshElements() is called, the this.searchForm and this.searchInput properties will be set on the controller and are subsequently available for event binding and DOM manipulation.

You can see a full example of this in this book’s accompanying files, in assets/ch04/views.html.

Delegating Events

We can also take a stab at cleaning up all that event binding and proxying by having an events object that maps event types and selectors to callbacks. This is going to be very similar to the elements object, but instead will take the following form:

events: {
  "submit form": "submit"

Let’s go ahead and add that to our SearchView controller. Like refreshElements(), we’ll have a delegateEvents() function that will be called when the controller is instantiated. This will parse the controller’s events object, attaching event callbacks. In our SearchView example, we want the search() function to be invoked whenever the view’s <form /> is submitted:

var exports = this;

  exports.SearchView = Controller.create({
    // Map all the event names, 
    // selectors, and callbacks
    events: {
      "submit form": "search"

    init: function(){
      // ...

    search: function(e){ /* ... */ },

    // Private

    // Split on the first space
    eventSplitter: /^(\w+)\s*(.*)$/,

    delegateEvents: function(){
      for (var key in {
        var methodName =[key];
        var method     = this.proxy(this[methodName]);

        var match      = key.match(this.eventSplitter);
        var eventName  = match[1], selector = match[2];

        if (selector === '') {
          this.el.bind(eventName, method);
        } else {
          this.el.delegate(selector, eventName, method);

Notice we’re using the delegate() function inside delegateEvents(), as well as the bind() function. If the event selector isn’t provided, the event will be placed straight on el. Otherwise, the event will be delegated, and it will be triggered if the event type is fired on a child matching the selector. The advantage of delegation is that it often reduces the amount of event listeners required—i.e., listeners don’t have to be placed on every element selected because events are caught dynamically when they bubble up.

We can push all those controller enhancements upstream to our Controller library so they can be reused in every controller. Here’s the finished example; you can find the full controller library in assets/ch04/finished_controller.html:

  var exports = this;

    exports.SearchView = Controller.create({
      elements: {
        "input[type=search]": "searchInput",
        "form": "searchForm"

      events: {
        "submit form": "search"

      init: function(){ /* ... */ },

      search: function(){
        alert("Searching: " + this.searchInput.val());
        return false;

    new SearchView({el: "#users"});

The best content for your career. Discover unlimited learning on demand for around $1/day.