Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training,
learning paths, books, tutorials, and more.

Chapter 4. Controllers and State

Historically, state was managed server side with session cookies. So, whenever
users navigated to a new page, the previous page’s state was lost—only the
cookies persisted. JavaScript applications, however, are confined to a
single page, which means we can now store state on the client’s
memory.

One of the major advantages to storing state on the client is a really
responsive interface. A user gets immediate feedback when interacting with
the page, rather than waiting a few seconds for the next page to load. Speed
greatly improves the user experience, making many JavaScript applications a
real pleasure to use.

However, storing state on the client causes challenges as well. Where
exactly should it be stored? In local variables? Perhaps in the DOM? This is
where a lot of developers get led astray, which is an unfortunate state of
affairs because storing state properly is one of the most critical areas to
get right.

First, you should avoid storing data or state in the DOM. That’s just
a slippery slope leading to an entangled mess and anarchy! In our case—since
we’re using the tried and tested MVC architecture—state is stored inside our
application’s controllers.

What exactly is a controller? Well, you can think of it as the glue
between the application’s views and models. It’s the only component aware of
the application’s views and models, tying them together. When the page
loads, your controller attaches event handlers to views and processes
callbacks appropriately, interfacing with models as necessary.

You don’t need any libraries to create controllers, although they can
be useful. The only essential part is that controllers are modular and
independent. Ideally, they shouldn’t be defining any global variables,
instead functioning as fairly decoupled components. An excellent way of
ensuring this is with the module pattern.

Module Pattern

The module pattern is a great way to encapsulate logic and
prevent global namespace pollution. It’s all made possible by anonymous functions,
which are arguably the single best feature of JavaScript. We’ll just
create an anonymous function and execute it immediately. All the code
residing within the function runs inside a closure, providing a local and
private environment for our application’s variables:

(function(){
/* ... */
})();

We have to surround the anonymous function with braces () before we can execute it. JavaScript requires this so it
can interpret the statement correctly.

Global Import

Variable definitions inside the module are local, so they
can’t be accessed outside in the global namespace. However, the application’s global variables are all still
available, and they can be readily accessed and manipulated inside the
module. It’s often not obvious which global variables are being used by
a module, especially when your modules get larger.

In addition, implied globals are slower to resolve because the
JavaScript interpreter has to walk up the scope chain to resolve them.
Local variable access will always be faster and more efficient.

Luckily, our modules provide an easy way to resolve these
problems. By passing globals as parameters to our anonymous function, we
can import them into our code, which is both clearer and faster than
implied globals:

(function($){
/* ... */
})(jQuery);

In the example above, we’re importing the global variable jQuery into our module and aliasing it to
$. It’s obvious which global
variables are being accessed inside the module, and their lookup is
quicker. In fact, this is the recommended
practice whenever you want to use jQuery’s $ shortcut, which ensures that your code won’t conflict with any
other libraries.

Global Export

We can use a similar technique when it comes to exporting
global variables. Ideally, you should be using as few global variables
as possible, but there’s always the odd occasion when they’re needed. We
can import the page’s window into our
module, setting properties on it directly, thereby exposing variables
globally:

The fact that we’re using a variable called exports to set any global variables means the
code is clearer, making it obvious which global variables a module is
creating.

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:

(function(){
assertEqual( this, window );
})();

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

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:

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():

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:

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:

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

var mod = new Controller;
mod.include(StateMachine);

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:

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:

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:

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:

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:

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:

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:

State Machines

State machines—or to use their proper term, Finite
State Machines (FSMs)—are a great way to program UIs. Using state machines, you can easily manage multiple
controllers, showing and hiding views as necessary. So, what exactly is a
state machine? At its core, a state machine consists of two things: states
and transitions. It has only one active state, but it has a multitude of
passive states. When the active state switches, transitions between the
states are called.

How does this work in practice? Well, consider having a few
application views that need to be displayed independently—say, a view for
showing contacts and a view for editing contacts. These two views need to
be displayed exclusively—when one is shown, the other view needs to be
hidden. This is a perfect scenario to introduce a state machine because it
will ensure that only one view is active at any given time. Indeed, if we
want to add additional views, such as a settings view, using a state
machine makes this trivial.

Let’s flesh out a practical example that will give you a good idea
of how state machines can be implemented. The example is simple and
doesn’t cater to different transition types, but it is sufficient for our
needs. First, we’re going to create an Events object that will use jQuery’s event API
(as discussed in Chapter 2) to add the ability
to bind and trigger events on our state machine:

The Events object is essentially
extending jQuery’s existing event support outside the DOM so that we can
use it in our own library. Now let’s set about creating the StateMachine class, which will have one main
function, add():

The state machine’s add()
function adds the passed controller to the list of states and creates an
active() function. When active() is called, the active state will
transition to the controller. The state machine will call activate() on the active controller and deactivate() on all the other controllers. We
can see how this works by creating two example controllers, adding them to
the state machine, and then activating one of them:

The state machine’s add()
function works by creating a callback for the change
event, calling the activate() or deactivate() function, depending on which is appropriate. Although the
state machine gives us an active()
function, we can also change the state by manually triggering the
change event:

sm.trigger("change", con2);

Inside our controller’s activate() function, we can set up and display
its view, adding and showing elements. Likewise, inside the deactivate() function, we can tear down anything
that is hiding the view. CSS classes offer a good way of hiding and
showing views. Simply add a class—say, .active—when the view is active, and remove it
upon deactivation:

Routing

Our application is now running from a single page, which
means its URL won’t change. This is a problem for our users because
they’re accustomed to having a unique URL for a resource on the Web.
Additionally, people are used to navigating the Web with the browser’s
back and forward buttons.

To resolve this, we want to tie the application’s state to the URL.
When the application’s state changes, so will the URL. The reverse is
true, too—when the URL changes, so will the application’s state. During
the initial page load, we’ll check the URL and set up the application’s
initial state.

Using the URL’s Hash

However, the page’s base URL can’t be changed without
triggering a page refresh, which is something we’re trying to avoid.
Luckily, there are a few solutions. The traditional way to manipulate
the URL was to change its hash. The hash is never sent to the server, so
it can be changed without triggering a page request. For example, here’s
the URL for my Twitter page, the hash being #!/maccman:

If the URL doesn’t have a hash, location.hash is an empty string. Otherwise,
location.hash equals the URL’s hash fragment, prefixed
with the # character.

Setting the hash too often can really hurt performance, especially
on mobile browsers. So, if you’re setting it frequently—say, as a user
scrolls through a list—you may want to consider throttling.

Detecting Hash Changes

Historically, changes to the hash were detected rather
crudely with a polling timer. Things are improving, though, and modern
browsers support the hashchange event. This is fired on the window, and you can listen for it in order to
catch changes to the hash:

When the hashchange event
fires, we can make sure the application is in the appropriate state. The
event has good cross-browser support, with implementations in all the
latest versions of the major browsers:

IE >= 8

Firefox >= 3.6

Chrome

Safari >= 5

Opera >= 10.6

The event isn’t fired on older browsers; however, there’s a useful
jQuery plug-in that adds the hashchange
event to legacy browsers.

It’s worth noting that this event isn’t fired when the page
initially loads, only when the hash changes. If you’re using hash
routing in your application, you may want to fire the event manually on
page load:

Ajax Crawling

Because they don’t execute JavaScript, search engine
crawlers can’t see any content that’s created dynamically. Additionally,
none of our hash routes will be indexed; as in the eyes of the crawlers,
they’re all the same URL—the hash fragment is never sent to the
server.

This is obviously a problem if we want our pure JavaScript
applications to be indexable and available on search engines like
Google. As a workaround, developers would create a “parallel universe”
of content. Crawlers would be sent to special static HTML snapshots of
the content, while normal browsers would continue to use the dynamic
JavaScript version of the application. This resulted in a lot more work
for developers and entailed practices like browser sniffing, something
best avoided. Luckily, Google has provided an alternative: the Ajax Crawling
specification.

Let’s take a look at my Twitter profile address again (notice the exclamation mark after
the hash):

http://twitter.com/#!/maccman

The exclamation mark signifies to Google’s crawlers that our site
conforms to the Ajax Crawling spec. Rather than request the URL
as-is—excluding the hash, of course—the crawler translates the URL into
this:

http://twitter.com/?_escaped_fragment_=/maccman

The hash has been replaced with the _escaped_fragment_ URL parameter. In the
specification, this is called an ugly URL, and it’s
something users will never see. The crawler then goes ahead and fetches
that ugly URL. Since the hash fragment is now a URL parameter, your
server knows the specific resource the crawler is requesting—in this
case, my Twitter page.

The server can then map that ugly URL to whatever resource it
represented and respond with a pure HTML or text fragment, which is then
indexed. Since Twitter still has a static version of their site, they
just redirect the crawler to that.

Because Twitter is using a temporary redirect (302) rather than a
permanent one (301), the URL shown
in the search results will typically be the hash address—i.e., the
dynamic JavaScript version of the site (http://twitter.com/#!/maccman). If you don’t
have a static version of your site, just serve up a static HTML or text
fragment when URLs are requested with the _escaped_fragment_ parameter.

Once you’ve added support for the Ajax Crawling spec to your site,
you can check whether it’s working using the Fetch
as Googlebot tool. If you choose not to implement the scheme on
your site, pages will remain indexed as-is, with a good likelihood of
not being properly represented in search results. In the long term,
however, it’s likely that search engines like Google will add JavaScript
support to their crawlers, making schemes like this one
unnecessary.

Using the HTML5 History API

The History API is part of the HTML5 spec and essentially
allows you to replace the current location with an arbitrary URL. You
can also choose whether to add the new URL to the browser’s history,
giving your application “back button” support. Like setting the
location’s hash, the key is that the page won’t reload—its state will be
preserved.

Supported browsers are:

Firefox >= 4.0

Safari >= 5.0

Chrome >= 7.0

IE: no support

Opera >= 11.5

The API is fairly straightforward, revolving mostly around the
history.pushState()
function. This takes three arguments: a data object, a title, and
the new URL:

The three arguments are all optional, but they control what’s
pushed onto the browser’s history stack:

The data object

This is completely arbitrary—you specify any custom object
you want. It’ll be passed along with a
popstate event (which we’ll cover in depth
later).

The title argument

This is currently ignored by a lot of browsers, but
according to the spec will change the new page’s title and appear
in the browser’s history.

The url argument

This is a string specifying the URL to replace the browser’s
current location. If it’s relative, the new URL is calculated
relative to the current one, with the same domain, port, and
protocol. Alternatively, you can specify an absolute URL, but for
security reasons, it’s restricted to the same domain as the
current location.

The issue with using the new History API in JavaScript
applications is that every URL needs a real HTML representation.
Although the browser won’t request the new URL when you call history.pushState(), it will be requested if
the page is reloaded. In other words, every URL you pass to the API
needs to exist—you can’t just make up fragments like you can with
hashes.

This isn’t a problem if you already have a static HTML
representation of your site, but it is if your application is pure
JavaScript. One solution is to always serve up the JavaScript
application regardless of the URL called. Unfortunately, this will break
404 (page not found) support, so
every URL will return a successful response. The alternative is to
actually do some server-side checking to make sure the URL and requested
resource is valid before serving up the application.

The History API contains a few more features. history.replaceState() acts exactly the same
as history.pushState(), but it
doesn’t add an entry to the history stack. You can navigate through the
browser’s history using the history.back() and history.forward() functions.

The popstate event mentioned earlier is triggered when the page is loaded or
when history.pushState() is called.
In the case of the latter, the event
object will contain a state property
that holds the data object given to history.pushState():

You can listen to the event and ensure that your application’s
state stays consistent with the URL. If you’re using jQuery, you need to
bear in mind that the event is normalized. So, to access the state
object, you’ll need to access the original event: