3.2 $.Controller - Templated Event Binding
JavaScriptMVC 3.2 brings a lot of great features and enhancements. So many features that changes to $.Controller didn't make the cut for our upcoming 3.2 article. This article reviews 3.2's $.Controller and talks about templated event binding (something we neglected to write up for 3.1).
Bind and Memory LeaksNeglecting to unbind event handlers is the easiest way to create memory leaks. This is extremely common in an MVC architecture as you constantly listen for model changes:
Task.bind('created', function(ev, newTask){
// add task to list
})
If your widgets are repeatedly added and removed from the page, you must remember to unbind these event handlers. People forget it all the time! This happens because simple jQuery plugins (which people use as template and learning tools) typically will not leak.
Simple plugins bind only on elements within the element the plugin was called on. jQuery cleans up these event handlers automatically. For example, a tabs might look like:
$.fn.tabs = function(){
// listen when an li is clicked and show tab content
this.find('li').bind('click', function(){
// show tab content
})
}
$('#tabs').tabs()
If the #tabs element is removed like:
$('#tabs').remove()
jQuery will remove all event handlers on the element and in the element's children. But, if you are listening to anything outside the widget's element (say the <body> or a Model event), jQuery will not remove the event handler. Lets explore this with a leaking tooltip:
A Leaking TooltipTo understand the problem of event handler leaking, consider a simple tooltip widget. The tooltip works by calling:
$('#tooltip').tooltip("Here is the text")
This will write "Here is the text" to the bottom right of the #tooltip element. The code for this tooltip looks like:
$.fn.tooltip = function(html){
var el = $('<p>').html(html),
offset = this.offset();
el.appendTo(document.body)
el.offset({
top: offset.top + this.height(),
left: offset.left + this.width(),
position: 'absolute'
})
$(document.body).click(function(){
el.remove();
})
}
See what's wrong? This code does not error, it leaks! If you can't spot the leak, don't feel bad. We've seen this mistake many, many times.
The problem is that although the element is removed, the body's click handler is not unbound. This function is still referenced by the DOM. And worse, this function has the paragraph element in its closure. The paragraph element and its child nodes will be kept in memory until the page refreshes.
Unbind with jQueryjQuery helps you unbind event handlers in a number of ways:
Remove the bound elementIf you remove an element from the DOM, all of its event handlers will be cleaned up. For example:
$('#foo').click(function(){
// event handler code
});
// sometime later
$('#foo').remove()
However, this only works if you are using jQuery's DOM modifiers to remove the element like:
$('#foo').parent().html("");
$('#foo').parent().empty();
$('#foo').replaceWith("<p>Some Content</p>");
This is why you should rarely use direct DOM methods like:
$('#foo')[0].parentNode.removeChild( $('#foo')[0] );
If you do this, your event handler will sit around in memory forever! Also, sometimes you do not want (or can't) to remove the bound element. So jQuery has other options:
Unbind directlyjQuery, of course, lets you unbind an event handler with unbind. When our tooltip is removed, we can unbind the body's click handler like:
$(document.body).click(function(){
el.remove();
$(document.body).unbind('click', arguments.callee)
})
Note: be very careful to pass in the same function to unbind as you passed to bind (arguments.callee happens to be this function); otherwise, jQuery will not unbind your event handler and you will continue to have a leak.
If you only handle the event once, one(event, handler) will unbind call for you. We can use that to listen to body clicks and avoid leaking like:
$(document.body).one(function(){
el.remove();
})
Finally, jQuery provides namespaced event handlers. It let you unbind all event handlers on an element that match a particular namespace. We could use namespaces like:
$(document.body).bind('click.tooltip',function(){
el.remove();
$(document.body).unbind('click.tooltip')
})
Problems
So far, this might seem ok, but there are lot of potential problems. Most importantly, there's a lot of waste! For every bind, there needs to be an unbind. You are double-coding. In our experience, few think about memory leaks and cleanup until it's too late.
ControllerController has always been useful for binding and unbinding. 3.1 brought templated event binding. This lets you bind and delegate on elements outside the controller's parent element. We can rewrite tooltip like:
$.Controller('Tooltip',{
init : function(element, message){
this.element.html(message)
},
"{document.body} click" : function(){
this.element.remove();
}
})
$('#info').tooltip("Search Google!");
Notice the "{document.body} click". This does exactly what you think it does and when the controller is destroyed, it will automatically unbind the document.body click handler. No double coding!
We can change Tooltip to use different elements to hide itself too. The following accepts an optional 'hideElement' option while keeping the document.body as a default:
$.Controller('Tooltip',{
defaults : { hideElement : document.body }
},
{
init : function(element, opts){
this.element.html(opts.message)
},
"{hideElement} click" : function(){
this.element.remove();
}
})
// use clicks on document.body to hide
$('#info').tooltip({ message: "Search Google!"});
// use clicks on #contentArea to hide
$('#wikiInfo').tooltip({
hideElement: $('#contentArea'),
message: 'Search Wikipedia'
});
How it works
When controller finds {NAME} in a prototype method like "{NAME} click", it looks in two places:
- this.options - First, it uses NAME to look up a value on the controller instance's options.
- window - If a value is not found, it uses NAME to look up a value on the window object.
The match can be one of two types:
- An object - If an object is found, it binds or delegates on that object instead of using the controller's parent element. This could be another element, or any object that has events triggered on it (a Model for example).
- A string - If a string is found, it just replaces {NAME} with the value
of that string.
You can use a string template to configure the type of event that hides the element:
$.Controller('Tooltip',{
defaults : { hideElement : document.body }
},
{
init : function(element, opts){
this.element.html(opts.message)
},
"{hideElement} {hideEvent}" : function(){
this.element.remove();
}
})
// hide on hoverenter
$('#info').tooltip({
hideEvent: 'hoverenter',
message : 'stop moving to make me go away'
})
Updating options
New in 3.2 is the ability to update options and templated event handlers with controller's update(options) method. If a controller is already bound to an element, calling its jQuery helper with options calls update(options). So, we can update the hideEvent and hideElement like:
// first time, tooltip calls init
$('#info').tooltip({
message : 'stop moving to make me go away',
hideEvent: 'click'
})
// second time, tooltip calls update
$('#info').tooltip({
hideEvent: 'hoverenter',
hideElement : $('#closer')
})
If we want to update the message, we can overwrite update to do so:
$.Controller('Tooltip',{
defaults : { hideElement : document.body }
},
{
init : function(element, opts){
this.element.html(opts.message)
},
"{hideElement} {hideEvent}" : function(){
this.element.remove();
},
update : function(opts){
this._super(opts)
this.element.html(opts.message)
}
})
Templated and MVC
MVC apps are constantly listening to changes in the $.Model layer to reflect changes in the UI. Templated event handlers make it stupidly easy to write abstract widgets that work with any model. An abstract list might look like:
$.Controller('List',{
init : function(){
this.element.html(this.options.template, this.options.list)
},
update : function(options){
this._super(options)
this.element.html(this.options.template, this.options.list)
},
"{list} add" : function(list, ev, added){
this.element.append(this.options.template, added)
},
"{list} remove" : function(list, ev, removed){
removed.elements(this.element).remove()
}
"{list} updated" : function(list, ev, item){
item.elements.replaceWith(this.options.template, [item] );
}
})
You can create list widgets that respond to changes in a $.Model.List like:
Task.findAll({}, function(tasks){
$('#tasks').list({list: tasks, template: 'tasks.ejs'})
})
People.findAll({}, function(people){
$('#people').list({list: people, template: 'people.ejs'})
})
And update the list with a new list like:
Task.findAll({personId: 1}, function(tasks){
$('#tasks').list({list: tasks})
})
The PlayerMX and Todo apps are very good examples of using templated event handlers.
ConclusionTemplated event handlers have made a big difference in how we write our apps. We've abandoned OpenAjax's pub-sub for direct Model events. We rarely use callbacks on model.destroy() or model.save() like:
model.destroy(function(){
// remove element!
})
and instead listen for changes like:
"{model} destroy" : function(){
// remove element!
}
This makes the remove element! code run no matter how the model instance gets destroyed.
Finally, templated event handlers lead to some of the bigger 3.2 changes: $.Observe and $.route.
Enjoy!
Deferreds and 3.1
jQuery 1.6 brought Deferred support. They are a great feature that promise to make a lot of asynchronous functionality easier to write and manage. But many people struggle with uses other than 'waiting for a bunch of Ajax requests to complete'. For JavaScriptMVC 3.1, we identified an extremely common but annoying practice that becomes a one-liner with deferreds: loading data and a template and rendering the result into an element.
Templates Consume DeferredsHere's rendering templates looks like in 3.1:
$('#todos').html('temps/todos.ejs',
$.get('/todos',{},'json') );
This will make two parallel ajax requests. One request is made for the template at temps/todos.ejs:
<% for(var i =0; i < this.length; i++) { %>
<li><%= this[i].name %></li>
<% } %>
The second request for /todos might respond with a JSON array:
[
{"id" : 1, "name": "Take out the Trash"},
{"id" : 2, "name": "Do the Laundry"}
]
When both have been loaded, the template is rendered with the todos data. The resulting HTML is placed in the #todos element.
This is fab fast! The AJAX and template request are made in parallel and rendered when both are complete. Before deferreds, this was a lot uglier:
var template,
data,
done = function(){
if( template && data ) {
var html = new EJS({text: template})
.render(data);
$('#todos').html( html )
}
}
$.get('temps/todos.ejs', function(text){
template = text;
done();
},'text')
$.get('/todos',{}, function(json){
data = json
done();
},'json')
Models Return Deferreds
Model AJAX functions now return deferreds. Creating models like:
$.Model('User',{
findAll: '/users'
},{});
$.Model('Todo',{
findAll: '/todos'
},{})
Lets you request todos and users and get back a deferred that can be used in functions that accept deferreds like $.when:
$.when( User.findAll(),
Todo.findAll() )
Or $.View:
$('#content').html('temps/content.ejs',{
users : User.findAll(),
todos: Todo.findAll()
})
Conclusion
Despite using templates, this is still 'waiting for a bunch of Ajax requests to complete'. Does anyone have other good uses for deferreds?
jQuery Resize Event
When building web applications, it can be tricky to get pages to layout correctly, especially when layout can't be done with CSS. Widgets that can dynamically change the layout only complicate matters. JavaScriptMVC 3.1 packs a new resize event that greatly simplifies these layouts.
The PastIn the long long ago, the before time, we would have various controls listening to the window's resize event and sizing themselves appropriately. But, eventually we discovered several problems or difficulties with this approach:
Problem 1: Callback OrderingIf you look at the demo page, you'll notice there are several elements that need to be resized:
- The RED Border element (fills the window)
- The BLUE Border element (fills the RED Element)
- The Splitter content elements
- The Scrollable element in the table
Each of these elements need to be resized after their parent elements have been resized. For example, the BLUE element needs to know the final size of the RED element before calculating its size.
We could be very careful to register and maintain all event handlers in the right order, but this would be tricky, error prone, and downright annoying.
Instead, what's needed is a resize event that fires in document order. That is, resize is fired first on the window, then the document, and then all child elements from the outside in.
But, this is opposite of how DOM events and jQuery's synthetic event system (trigger) works. The DOM calculates all the elements between the document and the target and then fires event handlers in two passes:
- Walks from document to target, firing event handlers listening to the 'capture' phase.
- Walks back from the target to the document, firing event handlers listening to the 'bubble' phase.

jQuery's trigger only bubbles. And, even if it supported a capture phase, it would not help. A resize event doesn't have a single target; instead, every element is a potential resize target!
So, instead of capturing or bubbling, our resize event can be thought of 'cascading' the document - firing every resize listener in order from parent to child.
Note If you are worried about this performing ... well, don't. We've got it covered.
Problem 2: Widgets That Resize Children.As the demo ... um ... demonstrates, we often have widgets (ex: splitters) that can adjust the size of their child elements. We could have these widgets trigger a resize event on the window which would call every resize handler. But, this would be inefficient. Only the elements within our widget might need to adjust themselves.
In the past, we handled this by having child widgets to resize and the parent widget trigger resize on the child widgets:
subElement.triggerHandler('resize');
This worked, but there were cases where subElement wasn't the element that had the resize event handler.
Instead, we wanted something where we could trigger resize on an element and any of its own or its child elements resize handlers would be called:
subElement.resize()Problem 3: Browser Differences
There is no standard around the resize event. IE fires resize events on elements, all other browsers only fire a resize event on the window.
Furthermore, it was easy in IE to create recursive resize events that would freeze the browser. You just would have to listen to resize and change the DOM so it would fire another resize event.
Naturally, we fixed all these problems.
UseDownload or steal the jquery/event/resize plugin. Then listen to resize like:
$('#foo').resize(function(){
// make the element the right size
});
If you didn't need to resize yourself, you can stopPropagation(), preventing your child elements from getting a resize event:
$('#foo').resize(function(ev){
if( IdontNeedToResize ){
ev.stopPropagation()
}
});
Finally, if you want to call child element's resize event handlers outside of a window resize event, simply call resize on the child:
child.resize();How it works
When an element first listens to resize, that element is inserted into array of elements sorted by document order.
When a resize event happens, resize is call on elements in that array. If the outer element being resized is not the document, it only calls event handlers on elements within the outer element.
What about Ben Alman's Resize Plugin?Ben's fantastic resize plugin lets you listen to when an element has been resized by polling for element width and height changes. This is great because you don't have to call resize if you change the dimensions of an element. However, it has 3 potential drawbacks:
Checking width and height is relatively expensive in some browsers. If you have lots of elements listening to resize, this could cause slowness.
To create smooth-ish resizing, you need all resize events to fire immediately one after the other. If your outer widget sets the width of an inner element, it could be another 250ms until that element resizes itself and its child elements.
Inner elements have to listen to parent resizes.
Ben's widget is a good choice for pages that don't have many nested structures that need to be resized.
ConclusionThe resize event has made our lives a lot easier. We've created a number of splitter, filler, resizable, and grid like widgets that exploit its power. Hopefully you can use it to clean up this often tricky part of JS application development.
Having your Cake and Eating it without Getting Fat
On stack overflow, someone asked what are the pros and cons of using JavaScriptMVC. Believe it or not, there no cons for using JavaScriptMVC.
But wait. "How can that be?" you ask. Certainly, everything has cons. Even ice cream makes you fat. Well ... you are wrong.
Actually I lied. JavaScriptMVC actually has just one drawback. It's "sorta" dependent on jQuery. You can use its testing, dependency management, and documentation engine with out the ubiquitous $. But if you want it's MVC goodness, DOM extensions, and special events, you must use jQuery.
But, I'll make the assumption that's not a big drawback to most people exploring JavaScriptMVC and explain why JavaScriptMVC is chockablock full of WIN.
FoundationsAt my roots, I'm a hard core enginerd. I don't believe in compromises. I think it's possible to have a flexible, lightweight, fast-performing framework, that scales like spiderman. I think JavaScriptMVC almost perfectly strikes that balance.
FlexibilityJavaScriptMVC is actually the MOST flexible framework that I know. You can use every part without every other part.
- You just want controller? Download standalone.
- Want to use JMVC with RequireJS? Here's an example.
- Got a favorite template engine? It comes packed with 3 of them.
- Want to package your app for sharing so people don't need Steal? Run:
js steal\pluginify app
It's hards to find cons in a framework that lets you use just the parts you want. If you find something better, use it instead and let us know about it. We do the same thing! It's why we switched from Prototype to jQuery and a 1000 other changes.
Besides using just what you need, JMVC's components are also very flexible. Lets use jQuery.Model as an example. The easiest way to create a model that connects to a JSON REST service is like:
$.Model('Recipe',{
findAll: "/recipes",
create: "/recipes",
update: "/recipes/{id}",
destroy: "/recipes/{id}"
},{
// a helper method
isTasty: function(){
return /mushroom/.test( this.title );
}
})
This allows you to do something like:
Recipe.findAll({}, function(recipes){
recipes[0].isTasty() //-> boolean
})
And now models support $.Deferreds allowing:
var recipesD = Recipe.findAll();
recipesD.done(function(recipes){
recipes[0].isTasty()
})
But what if your service is ... God forbid ... XML? JavaScriptMVC is flexible enough to connect to any service layer:
$.Model('Recipe',{
findAll: function(params, success, error){
return $.ajax({
url: "/recipes.xml",
dataType : "xml json recipe.models"
})
},
...
)
Here's an example of a Model that uses localStorage. Controllers can listen to jQuery events, OpenAjax events, and be made to work with any other event system. You can register any template engine to work with $.View and Steal's build system.
It's damn hard to find cons in a framework that has sane defaults that can be abandoned if necessary.
LightweightAt the always awesome JSConf, John Hann, angry at frameworks, made the case for modules instead of frameworks and libraries. He's upset that trying to use a single part of a framework brings in too much unused code. This even made it to readwriteweb.
Mr. Hann should love JavaScriptMVC as there is almost none of this. JavaScriptMVC has no 'Core'. Each component loads only what it needs. For example, unlike jQuery UI, JMVC's default drag does not have the ability to limit it's area, to move in steps, or to scroll an area that it hovers over. In JMVC, those are plugins. No waste!
Fast PerformingJavaScriptMVC doesn't take any chances with performance or memory. Read this article why JMVC renders as fast as possible.
JavaScriptMVC is also the easiest way of avoiding memory leaks. This is a whole 'nother article that needs to be written. But take my word for it, if you use JavaScriptMVC in the default way, with controllers doing the binding, it is almost impossible to create leaks. And if you aren't using $.Controller, you are almost certainly leaking memory. This article shows a short example. But it doesn't touch on how JavaScriptMVC can automatically clean your model instances when they are no longer being used. This is a HUGE problem for long-lived apps that load 1000s of records. JMVC does not have this problem. Do you?
ScalesBecause of its flexibility, if your app is small, you can use just a few parts of JavaScriptMVC. However, if it turns into an unruly monster, it's got your back. Everyone writing jQuery should be using $.Controller. But if you need testing, pull in FuncUnit. If you need dependency management, use steal. If you need performance, use $.curStyles. The server team is being slow, grab $.fixture.
More than likely, JavaScriptMVC has solved your problems (excluding widgets). If you need help, we are extremely active on the forums. If you need even more help, you can hire us!
ConclusionDespite an ugly name and (until recently) website, at its foundations, JavaScriptMVC is almost an ideal framework for jQuery. You can use just what you want without any penalty. What could be better?
Knock JavaScriptMVC's Back Out
I've read a lot of articles and tweets comparing JavaScriptMVC, BackboneJS, and KnockoutJS. A lot of good things can be said about these frameworks. Backbone is largely similar to JavaScriptMVC's MVC components, and Knockout has a really amazing observable system.
But it gets under my skin when people compare how easy it is to 'get up to speed on' these frameworks. JavaScriptMVC's API is, in my opinion, just as simple as any other framework. Here's how you setup a model to talk to your server:
$.Model('Todo',{
findAll : '/todos',
findOne : '/todos/{id}',
create: 'POST /todos',
update: 'PUT /todos/{id}',
destroy : 'DELETE /todos/{id}'
},{})
Here's how you can findAll those todos and render them in a list using an ejs template:
Todo.findAll(function(todos){
$('#todos').html('todoList.ejs',todos);
});
Here's a jQuery widget that listens for clicks on those todos and delete them from the server:
$.Controller('Todos',{
'a.destroy click' : function(el){
var todoEl = el.closest('.todo')
todoEl.model().destroy(function(){
todoEl.remove();
})
}
});
$('#todos').todos();
It's simple. But, JavaScriptMVC does have a much longer 'ramp' up speed than these frameworks simply because it's got almost everything. Take a look at its core features. It's getting started guide alone takes you through:
- creating files and folders for your app
- writing code for it
- documenting it
- testing it
- building a production build
Many apps might not need all this. But I promise that if yours does, you and your team's total ramp-up speed will be far faster with JavaScriptMVC than some piecewise collection.
I wouldn't try to dissuade anyone from trying out any other library or framework. Just make sure you are comparing apples to apples. Not an apple to an orchard. Of course, if you just want a few apples, you can download just JMVC's core MVC parts.
JavaScriptMVC and List Performance
Most applications center around displaying and interacting with lists and individual pieces of data. Consider applications like GMail, Grooveshark, Twitter, Facebook, and Hulu. Each of these applications displays a list of items where a user can drill down into a single item. In many cases, the user can edit a single item while in the list view.
Displaying a list of items is often the worst performing (in terms of time to display) part of an application. This is for multiple reasons:
- A list typically shows more data.
- Each itemâs html needs to be created and inserted into the DOM.
- Event handlers often need to be registered on each itemâs control.
From itâs earliest versions, JavaScriptMVC has been almost entirely about making the performance of displaying lists as fast as possible. In fact, no superior solution is likely possible. This article discusses how it accomplishes this feat.
InsertionWhen data is retrieved from the server, it needs to be converted into a form suitable for human interaction. With web development, this means HTML. That HTML needs to be inserted into the DOM for it to be visible.
For optimal performance across multiple browsers, the HTML should be created as a string and inserted into the DOM using a single innerHTML call (or a technique that uses innerHTML).
Furthermore, when building strings, itâs important to join an array of strings as opposed to concatonating strings (if you want to achieve good performance in IE).
JavaScriptMVCâs View layer ($.View) provides these optimizations with a very elegant syntax:
$(â#recipesâ).html( â//path/to/recipes.ejsâ,
[ {name: âice creamâ,id: 1},
{name: âhot dogâ, id: 2} ] )
In //path/to/recipes.ejs:
<%= for(var i =0; i < this.length;i++){ %>
<div class='recipe <%= recipe[i].identity()%>'>
<%= recipe[i].name %>
</div>
<% } %>
When creating a template, $.View creates a JS function that concatenates an array of strings. The function looks like:
var _v1ew = [];
for(var i =0; i < this.length;i++){
_v1ew.push(â<div class="ârecipe â,
recipe[i].identity(),">â)
_v1ew.push(recipe[i].name)
_v1ew.push(â</div>â)
}
return _v1ew.join(ââ);
Note, that in development mode, $.View makes a request for the template. However, you can package the processed form of your view into your production build like:
steal.views(â//path/to/recipes.ejsâ)
If you package your view, not only will no request be made, the view will not have to be processed!
In summary, $.View provides a highly performant use of client side templates with minimal syntax.
Responding to EventsOnce content has been inserted into the DOM, the user must be able to interact with it. Traditionally, a developer would have to add event handlers to the page. This would require performing additional expensive DOM manipulations and traversals to add the necessary event listeners.
However, JavaScriptMVC pioneered the use of event delegation which allows you to listen on another element for events on another element. We worked to bring JavaScriptMVCâs 1.5 event delegation into jQuery and we pushed extremely hard for $.fn.delegate.
$.Controller is designed around event delegation. This allows you to listen for events on a root element and does not require additional expensive DOM manipulations:
$.Controller(âMylistâ,{
â.recipe .delete clickâ : function(){
// delete the recipe
}
})
In summary, $.Controller removes the needs for additional expensive DOM manipulations by organizing delegated event handlers.
ConclusionDisplaying interactive lists is probably the most common âbad-performingâ functionality that is critical to user experience.
JavaScriptMVCâs $.View and $.Controller provide completely optimal solutions to displaying interactive lists while providing a straightforward and relatively simple syntax.
JavaScriptMVC Cheat Sheet
Creating a JavaScriptMVC Slider
JavaScriptMVC makes it easy to write and organize copious amounts of JavaScript code. And although this usually means working among its Model-View-Controller layers to create something amazing, often, you'll find yourself wanting to make a lightweight, reusable UI widget. This demo walks you through creating a basic slider widget.
Demo (What We're Making) Step 0: Following alongTo follow along with this guide, download a fresh copy of JavaScriptMVC and unzip it (or update your current copy).
Step 1: Creating a widget play-place.For the vast majority of applications we build, we have two root folders:
-
The application folder
Has all the files and tests specific to the application: models, business logic controllers, and views, used to customize the output of reusable widgets.
-
The widget folder
Has all the potentially reusable widgets. These are things that can be used outside a specific application. For Jupiter, we've been putting all of these in mxui, the jQueryMX UI library.
The first step of creating a widget is creating the folder and files our widget needs. Fortunately, the jquery/generate/plugin generator does this. By typing the following in the command line, it creates a page, empty source file, and tests for our widget.
js jquery/generate/controller mxui/sliderStep 2: Creating the API and test page.
Before you can write tests for a UI widget, it helps to know what you're going to test. So, the first step is to create a test page and design the widget's API.
Designing an API is a subtle and tricky business. Try to keep things as simple as possible by only building the 'essence' of a widget. For me, it's just a draggable element whose value changes based on its position relative to its container. The slider will read the container's properties and set itself up accordingly.
This is in contrast to something like jQueryUI's or YUI's slider, where the slider control includes the containing element and mandates other conventions (like specific classNames).
By focusing on just the basics, our slider will be lightweight and more flexible.
So, to our test page, I'll add a minimal HTML structure for the slider, some CSS, and an element to show the value of the slider:
The HTML:
<div id='container'> <div id='slider'></div> </div> <input id='value'/>
The CSS:
#container {
width: 330px;
border: solid 1px black;
}
#slider {
width: 26px;
border: solid 2px green;
background-color: #008000;
height: 30px;
}
Finally, I want to setup my slider and listen for changes on it. So I add the following in a script tag:
$("#slider").mxui_slider({
min: 1, // the minimum value
max: 10, // the maximum value
val: 5 // the starting value
}).change(function(ev, value){
// show the value
$('#value').val(value)
});
You can see the result of these pages in slider.html. Now we have something we can test!
Step 3: Creating a test.The generator produces a test script in mxui/nav/slider/slider_test.js . I'll add the following tests:
test("dragging changes value",function(){
S("#slider").drag("+30 +0", function(){
equals( S("#value").val(), 6);
}).drag("-60 +0",function(){
equals( S("#value").val(), 4);
});
})
test("dragging out of bounds", function(){
S("#slider").drag("+400 +0", function(){
equals( S("#value").val(), 10);
}).drag("-400 +0", function(){
equals( S("#value").val(), 1);
})
})
FuncUnit's syntax is so awesome, I'm not going to explain what these do. If I were making this slider for more than a demo, I'd also add quite a few more tests. But this is a good start.
If we run these tests, it fails miserably. So, lets move to building the slider!
Step 4: Creating the slider plugin:I'll build the plugin in steps that paralleled how I actually built the widget.
Setup the slider widgetThe first thing I'll do is open slider.js and steal the two plugins I'm certain are needed:
steal.plugins('jquery/controller',
'jquery/event/drag')
.then(function($){
Then I define my slider widget:
$.Controller("Mxui.Slider",{})
Configuring the drag behavior
I want my element to be draggable, so I'll make a draginit function:
$.Controller("Mxui.Slider",{
"draginit" : function(el, ev, drag){}
});
At this point, I can start dragging my element around the page. But, I want to keep the element in its container. JavaScriptMVC has a drag limit plugin that does exactly this. So I'll add 'jquery/event/drag/limit' to the list of steal.plugins and make draginit look like:
"draginit" : function(el, ev, drag){
drag.limit(this.element.parent())
}
At this point, my drag can't escape it's parent. With the following 5 lines, we've got something that looks very much like a slider:
$.Controller("Mxui.Slider",{
"draginit" : function(el, ev, drag){
drag.limit(this.element.parent())
}
});
I also want my slider to snap in increments. The jquery/event/drag/step plugin limits a drag's position to every X pixels relative to some container. This is perfect! The only problem is that I need to know that pixel value. To help, I'll create a getDimensions function that gets and caches this and other values:
getDimensions : function(){
var spots = this.options.max - this.options.min,
parent = this.element.parent();
//total movable area
this.widthToMove = parent.width() -
this.element.outerWidth();
//space between spots
this.widthOfSpot = this.widthToMove /
this.options.spots;
}
The following diagram might help these calculations make sense:
$.Controller creates a jQuery plugin. In this case, it creates $().mxui_slider(). The first argument passed to the mxui_slider becomes this.options on the controller. I used the min and max values, the width of the container element, and the outer width of the slider to calculate the width of one spot.
Now in draginit, I call getDimensions and use the spot width to set the step value:
"draginit" : function(el, ev, drag){
this.getDimensions();
drag.limit(this.element.parent())
.step(this.widthOfSpot, this.element.parent());
}
Calculating and sharing the value
I want my slider to behave similar to a form element. When it's value changes (a drag motion is complete), it will trigger a change event with the value of the slider.
To do this, I need to know where the draggable element is in relation to the inner left side of the container. I'll calculate this in getDimensions with JavaScriptMVC's curStyles plugin:
var styles = parent.curStyles("borderLeftWidth",
"paddingLeft"),
leftSpace = parseInt( styles.borderLeftWidth ) +
parseInt( styles.paddingLeft )|| 0;
this.leftStart = parent.offset().left + spaceLeft;
Now I can listen for dragend, which fires when the user finishes dragging, calculate the slider's value (which spot its in), and trigger a change event with the value:
"dragend" : function(el, ev, drag){
var left = this.element.offset().left - this.leftStart;
var spot = Math.round( left / this.widthOfSpot );
this.element.trigger("change", spot+this.options.min)
}
I also want to be able to get the value of the slider programmatically like:
$('#slider').mxui_slider('val') -> '5'
And be able to set the value (and update the UI) like:
$('#slider').mxui_slider('val',7);
To do this, I'll add a val function to my controller like:
val : function(value){
this.getDimensions();
if(value){
//move slider into place
this.element.offset({
left: this.leftStart+
Math.round( (value-this.options.min)
*this.widthOfSpot )
})
this.element.trigger("change", value)
}else{
var left = this.element.offset().left -
this.spaceLeft;
return Math.round( this.leftStart/this.widthOfSpot)+
this.options.min;
}
}
Model <--> View binding
As a quick side note, this plugin can now work with the 'jquery/tie' plugin so you can have bi-directional binding between models and sliders. For example:
$.Model('Person')
person = new Person({age : 7})
$('#slider').mxui_slider({min: 0, max: 10}).tie(person, "age")
This allows us to change the value of person and it will automatically update the slider and vice versa. See an example here.
Accept an initial valueWe want to be able to initialize our controller with a value. We'll add an init method that sets an inital value if provided:
init : function(){
this.element.css({
position: 'relative'
})
if(this.options.val){
this.val(this.options.val)
}
}
Default options
Finally, we want to make our plugin as easy to initalize as possible so we give it default option values like:
$.Controller("Mxui.Slider",{
defaults : {
min: 0,
max: 10
}
},
{ ... });
If users don't provide their own options, these are used.
Step 5: Sharing your sliderIf you update steal, or have a very recent download, you can run:
js steal/pluginifyjs mxui/slider -nojquery
To create a mxui.slider.js script that can be used without JavaScriptMVC and only requires jQuery.
ConclusionThis example is meant to touch on a few key concepts:
- Test driven development of traditionally difficult to test UI behavior.
- High-level JavaScriptMVC organization.
- Widget design.
- Using drag events and controller.
- Creating standalone widgets.
But most importantly, it might make you less afraid to write your own widgets. There's room for improvement. For example, it should only trigger a change event if the value changes. However, in only 53 lines, we've made a halfway decent slider that covers 95% of what most apps need.
JavaScriptMVC 3.0: Good To Go!
For the past year and a half, we've been pouring our heart into JavaScriptMVC 3.0. So it's with great excitement that we're ready to announce 3.0 is here to solve your JavaScript headaches. With over 1800 commits, the new version is faster, easier to use, and more powerful.
But with so much goodness, it's hard to know where to start. The following are the biggest and bad-ass-est changes to JavaScriptMVC:
FuncUnitFuncUnit is probably the best thing to happen to web testing ... ever. It makes automated tests extremely easy to write and debug. Tests look like jQuery code:
module('numeric',{
setup : function(){
S.open('numeric.html')
}
})
test('entering letters', function(){
S('#numeric').type('seven').then(function(){
ok( this.text() , "" , "input field is empty")
})
})
test('entering letters', function(){
S('#numeric').type(7).then(function(){
ok( this.text() , "7" , "input has 7")
})
})
You can run this test in your browser, or automated via selenium.
Standalone Sub ProjectsDespite JavaScriptMVC's tight integration, we've long understood that some people might not be able to use the entire framework. With 3.0, every part works standalone. So no matter the project, you can start benefitting from JavaScriptMVC's goodies. JavaScriptMVC is now the following 4 projects:
- DocumentJS - Documentation Engine (git) (forum)
- jQueryMX - jQuery MVC, DOM, and Event extensions (git) (forum)
- StealJS - Dependency Management, Code Generators, CSS & JS compression (git) (forum)
- FuncUnit - Web Testing Framework (git) (forum)
Each has its own website (coming soon), github repo, and forum. You can customize the jQueryMX download like jQueryUI and Mootools:

Of course, it all works a lot nicer together.
Multi-Page BuildsStealJS can optimize your scripts across several pages to get practically the fastest possible load times. We're not even aware of server-side dependency management tools that do this.
js steal/buildjs homepage profile tracks
This recursively finds the largest common dependencies between apps and puts them in a shared script. The result is the fewest number of scripts with the most optimal caching between pages.
Less and CoffeeScript SupportSick of being stuck with JavaScript and CSS for the past 15 years, but you don't want to compile every time you make changes? We thought so. We've made it really easy to integrate other languages and technologies into StealJS's dependency management and build process. For example, create a LESS file like:
.rounded_corners (@radius: 5px) {
-moz-border-radius: @radius;
-webkit-border-radius: @radius;
border-radius: @radius;
}
#header {
.rounded_corners;
}
#footer {
.rounded_corners(10px);
}
And a CoffeeScript file like:
# Conditions: number = -42 if opposite # Functions: square = (x) -> x * x
And you can load them into your app like:
steal.less('myless').coffee('mycoffee')
When it comes time to build, steal converts them into JS or CSS and packages them along with the rest of your files. But while developing, just refresh to see changes. Yep, it's amazing.
P.S. jQuery.View uses StealJS's ability to handle multiple languages to compress EJS, JAML, Micro, and jQuery.tmpl templates into your production package.
New Folder StructureThe secret to building large apps is to never build large apps. 3.0 is designed with this in mind. It makes it easy to build parts of your application as standalone plugins, each with their own demo page, tests, documentation, etc. Here's what our Srchr example looks like:
Notice how the each plugin (disabler and tabs shown here) has its own demo page, tests, and css. The final app simply 'steals' and wires up these plugins:
steal.plugins('srchr/disabler','srchr/tabs')
.then(function(){
// make a disable-able tab widget
});
Similarly, Srchr's test script just each plugin's test script to make a single test that runs all tests:
steal.plugins('srchr/disabler/funcunit',
'srchr/tabs/funcunit');
The result is it's extremely easy to develop and test part of your application in isolation and then integrate it with the rest of the app.
As a bonus, the new structure makes it easy to share (and use) other people's work. There are already third party plugins to use! If you're using git, you can add them as submodules or install them from the command line:
js steal/getjs mustacheLearn-ability
JavaScriptMVC has a ton of functionality. But, we're committed to making its learning curve as short and flat as possible. With the exception of DocumentJS (ironic), every component has:
We also:
- Moved our forums to Zoho.
- Provided a detailed example of a full application written in
JavaScriptMVC.

- Shared a bunch of example widgets with mxui.
In the short term, we're going to stay focused on making 3.0 easier to use and learn. This means more blog articles, videos, demos etc.
But, in the long term, there's a few things we'd like to tackle:
A recorder / IDE for authoring FuncUnit tests. This will be similar to Syn's demo.
An asyncronous events extension to jQuery. We want to be able to pause and resume events like:
$(".tab").delegate("li","activate",function(ev){
ev.pause();
$(this).load('/content', function(){
ev.resume();
})
})
This will make it easy to weave in asynchronous extensions to widgets.
An extremely lightweight UI/widget library. We've already got a good collection of rough widgets in mxui. You should be able to weave in the features you need.
Integration with RequireJS and LabJS. In an ideal world, we want a script loader with the:
- Namespace clean-ness of RequireJS
- Speed of LabJS
- Build features of StealJS
With 3.0, JavaScriptMVC is officially mature. We've added few purely new features from 2.0. Instead, we perfected existing features. FuncUnit is the best example of this. 2.0's testing could work in the browser and with Selenium. However, we:
- Fixed Selenium's numerous synthetic event bugs with Syn.
- Created a much nicer API - one that handles asynchronous functional programming extremely well.
- Threw out our own assertion library and replaced it with qunit.
- Made FuncUnit work stand-alone.
The result is that FuncUnit is very nearly the perfect web testing tool. And, as a result of hundreds of similar changes to the rest of the framework, JavaScriptMVC is now the perfect web development tool :-).
FuncUnit Infographic
FuncUnit is pretty awesome. But it might not be clear how it works in Browser vs Command (Selenium) mode. To help, I put together 2 quick illustrations that highlight what's going on.
First, lets look at what happens when you open a FuncUnit test page with your browser:
Browser Mode
Essentially, 'S' provides a bunch of methods for selecting elements in a popup window and performing actions on them or getting information about them. QUnit is used for reporting and conditionals.
Lets see what happens in Command Mode:

In command mode, we use Envjs to open the same FuncUnit page. Envjs is a 'headless' browser. A headless browser is a browser that runs in a command line and you can't see the rendered page. But you CAN interact with the page with JavaScript.
When a FuncUnit page is opened up with Envjs, it knows to start Selenium. Then when your tests are run, they use Selenium to issue commands to the popup window. By using Envjs Selenium, we automated the same process that happens in "Browser" mode.
ConclusionI hope this is helpful. It's rather rough. But if you find these 'infographics' helpful, we will produce more of them.