Monday, August 16, 2010

Good Practices for Rich Web Applications

Use jQuery

jQuery is the best thing that has happened to Javascript since it got first class functions in version 1.2. The library is elegant, powerful and has exactly the right level of abstraction for working with the DOM. There is nothing more to say. Learn it and use it. Good resources are: the jQuery API, my view of the jQuery API

Learn Javascript

Javascript is the programming language of the web. Learn it! Javascript is different from most other programming languages. It is dynamic, it has prototypical inheritance, and works more like Scheme than any of the languages that you are probably used to. If you want to learn Javascript you should get the following books, The Little Schemer, The Seasoned Schemer, Javascript, the Good Parts, and possibly High Performance Javascript

Learn CSS

Many programmers think that CSS is the language of designers and not programmers. This is not the case at all. If you are lucky enough to have a designer on your team (most people don't), CSS is the language with which you communicate. It is the interface between designers and programmers and as a programmer you should know it better than the designers. By knowing CSS well you will reduce the misunderstandings between you and your designer.

Unfortunately, many designers don't care about how code looks, as long as the design looks good on the surface. It will be up to you to make sure that the CSS doesn't get out out of hand. It will also be up to you to keep the HTML clean, and a good way to do this is to use semantic HTML, combined with CSS. You have no idea what the designers can come up with.

<!-- 
  Old School rounded corners, invented by a GOOD designer. 
  All this code was actually needed to achieve the purpose.
  -->
<style>
.t {background: url(dot.gif) 0 0 repeat-x; width: 20em}
.b {background: url(dot.gif) 0 100% repeat-x}
.l {background: url(dot.gif) 0 0 repeat-y}
.r {background: url(dot.gif) 100% 0 repeat-y}
.bl {background: url(bl.gif) 0 100% no-repeat}
.br {background: url(br.gif) 100% 100% no-repeat}
.tl {background: url(tl.gif) 0 0 no-repeat}
.tr {background: url(tr.gif) 100% 0 no-repeat; padding:10px}
</style>
<div class="t">
  <div class="b">
    <div class="l">
      <div class="r">
        <div class="bl">
          <div class="br">
            <div class="tl">
              <div class="tr">
                Lorem ipsum dolor sit amet consectetur adipisicing elit
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

As an additional benefit you will become better at jQuery. Not only is CSS the query language of the browsers it is the query language of jQuery. Jariba!

Bulletproof Web Design is a good book web design, including CSS.

Decide how "Rich" your application should be

How rich should your application be? The scale varies from no Javascript to only Javascript, but you will probably want to land somewhere in between. Here are a few suggestions.

  • No Javascript, everything is server generated.
  • Slightly enhanced pages, simple validations, but no Ajax.
  • Ajax enhanced pages, but every page still reloads frequently.
  • Single page per area, entire area is handled by Javascript.
  • Only Javascript, Ajax interaction with the server
  • Only Javascript, no interaction with the server.

The important thing is to make a decision. If you don't make the decision, everyone will do different things on different parts of the application and you will loose consistency. In GUI, consistency is king. Make a decision and move on, you can always change your decision later.

Organize your code

Javascript

Make sure that all your Javascript code is namespaced properly. It is impolite to pollute the global namespace and it will bite you in the end. A simple variable declaration will do.

// Common namespace for your entire application
// This declaration lets you split your code of multiple files.
// If MyNamespace is defined use it, otherwise declare it.
MyNamespace = MyNamespace || {};

But, of course, it is also possible to get fancy and encapsulate the functions that you don't want to expose, if that is your cup of tea.

MyNamespace = MyNamespace || {};

MyNamespace.Tournament = function() {
 // Private stuff
 var tournamentCount = 0;
 function addTournament(tournament) {
  tournamentCount++;
 }
 
 return {
  //public stuff
  numberOfTournaments: function() {
   return tournamentCount;
  }
 }
}();

You should also separate your Javascript code into different files. The namespace idiom above helps to have the same namespace across multiple files. The same principles as with other type of code is valid with Javascript, organize the code by area, when it changes, where it is used, etc. Don't be afraid of the additional load time, splitting the files will give you. The files can easily be concatenated with tools like Rake, SCons, Ant or even a simple:

$ cat file1.js file2.js file3.js > all.js

They can also be compressed with JSMin or YUI Compressor.

Optimize your environment for development, not for production!

HTML

HTML is code! Divide your pages into partials by responsibilities. It allows you to keep your pages DRY and readable. The Single Responsibility Principle applies to HTML too.

Make sure you keep the Javascript with the code that it manipulates. If you, for example, have a calendar partial that uses jQuery DatePicker, you have to make sure that the partial includes all the necessary Javascript to configure the calendar. Don't keep Javascript code in the page away from the partial. Things that change together should be together.

CSS

Stylesheets are code too. They should also be split into areas that allow you to easily find and navigate them. Use Sass or SCCS to keep your CSS files DRY. Sass is good for designers to. It gives them the ability to use variable names, mixins, etc. and simplifies their usage of semantic names such as notice, and sidebar instead of yellow and left.

Optimize your environment for development, not for production!

Separate your Javascript from your HTML

All too often I see generated HTML pages with Javascript code in them. Don't do it. Keep the HTML free from Javascript.

<!-- DON'T DO THIS!  -->
<button id='update-button' onclick="MyNamespace.updateList();">Update List</button>

// In the Javascript file for the page.
MyNamespace.updateList = function() {...}


<!-- DO THIS! -->
<button id='update-button'>Update List</button>

// In the Javascript file for the page.
MyNamespace.updateList = function() {...}

$(function() {
  $("#update-button").click(function() {
    MyNamespace.updateList();
  });
});

It may seem like there is a lot more code in the good example, but notice the symmetry. The code that attaches the listener is in the same file as the code that uses the listener. This is good. Symmetry is good.

Use clone()

Separating the HTML and the Javascript goes both ways. Don't generate HTML code in Javascript. It doesn't matter that it is super simple to do it using jQuery.html(). Keep them separate, use jQuery.clone() instead.

// DON'T DO THIS
$("<li data-id='123'>My new item</li>").appendTo("ul");
// OR THIS
$("ul").append("<li data-id='123'>My new item</li>");


<!-- DO THIS -->
<ul>
<li id="list-template" class="template">All .template are hidden (display: none) in the CSS</li>
</ul>

// AND THIS
var $clone = $("#list-template").clone();
$clone.attr("data-id", "123").text("My new item").removeClass("template");
$("ul").append($clone);

The point of this is, again, to keep the HTML separate from the Javascript.

Decide how content flows between, the page, Javascript and the Server.

Once you have decided how rich your application should be, you have to decide on a method for moving the data between the HTML Page, Javascript and the Server. My preferred choice is to have every page that is served from the server include a context object with all the static data for the page and to have additional data that belongs to parts of the page be sent as data-attributes on the elements concerned.

The context object will contain all the data that is commonly needed in the page.

// Sample context object that is generated with the page.
MyNamespace = MyNamespace || {};
MyNamespace.Context = {
  user: {
    id: "28",
    name: "Anders Janmyr"
  },
  tournament: {
    id: "78344",
    name: "Fifa World Cup"
  }
};

I use the context object(s) to keep state on the client to. If it is important that the page looks exactly the same, even if the user reloads the page, I make sure that the state I stick into my context object is synched back to the server. This can easily be done, asynchronously, with Ajax and does not affect performance noticeable.

Elements specific data is sent along with the element it defines.

<!-- Element specific data attached to the elements with data-attributes -->
<ul id="tournament-menu">
  <li data-id="78344" data-participant-count="64">Fifa World Cup</li>
  <li data-id="666"  data-participant-count="44">Americas Cup</li>
  <li data-id="1464" data-participant-count="32">Rugby World Cup</li>
</ul>

The same argument as with the context object applies, as soon as I change an element in the GUI i need to feed that information back to the server. With elements I usually send the information to the server before updating the GUI, since element specific data is usually permanent data and not just session data.

An alternative solution to the context object above is to use the body element as the data-container, like this:

<body data-user-id="28" data-user-name="Anders Janmyr" data-tournament-id="78344" data-tournament-name="Fifa World Cup">

I tend to use the context object because I find it easier to add functionality to it than to the DOM element.

Only send the data that is needed to the client with the page. The rest of the data should be loaded on demand, with Ajax. Both JSON data and HTML templates can be loaded on demand. There is no need to deliver the entire page at once. Experiment and do what gives the best user experience.

Use file watchers to speed up feedback

If you compare the feedback cycle of Javascript and HTML development to Java and C# development, you are probably very happy with the short, tight feedback loop. This doesn't mean that you should be content. A feedback cycle cannot be too short.

xrefresh for Firefox and IE, and LiveReload for Safari and Chrome, are a couple of tools that will tighten your feedback loop even more.

Both tools are file watchers that listen to changes for files and refresh the browser when they change. If you combine this with two screens, you will have one screen for the browser, that updates continuously, while you edit your code on the other screen. Fabulous!

Conclusion

Rich web applications are very close to the traditional client-server model. We have to keep state on the client side to give the user a good experience. At the same time the application state, and indeed the entire application, can be swept away by a click of a button or a page reload.

This puts new demands on us as developers. We have to realize that we are responsible for the entire application, not just the business logic, but the HTML and CSS too. More that anything, we have to realize that Javascript is a first class programming language with its own programming techniques, which we need to master to be able to develop good web applications.

2 comments:

Chris Hedgate said...

Thanks Anders, a must-read article for web developers and a reference to keep. Something I would like to hear more about, regarding good practices for rich web applications, is security. How do you handle code executing 'in clear sight' on the client, not only viewable but actually editable.

Anders Janmyr said...

@Chris, thanks,

Security is not really my area of expertise, but handle as much as possible on the server side. Anything that comes onto the page, can be manipulated.