Tag Archive for 'jQuery'

jQuery Datepicker – the Instance Data bug

The jQuery UI datepicker does strange things with the DOM, which causes undocumented brittleness. For instance, consider the following operations on a page that contains a single input element:

$('input').datepicker().attr('id','the-input');

This will cause no error, and clicking on the input will correctly summon the date picking dialog, but clicking on a date in that dialog will fail with the following error:

missing instance data for this datepicker

The diagnosis is quite simple: the jQuery UI datepicker stores additional “instance data” based on the id attribute of the element, so changing the id attribute manually causes that instance data to be lost. This unexpected brittleness forced me to spend some time hacking my code so that the identifier is attributed before the datepicker is enabled, but at least this solved the problem.

Two related problems would be:

  • If you have several input elements with the same identifier, and apply the datepicker on the second element, the search-by-id will return the first element and cause the same error as above.
  • If you apply the hasDatepicker CSS class on an element, and then apply the datepicker plugin, it will assume that the instance data has already been initialized, and will fail.

Financial Bubbles

People are used to financial data being represented as plots of value against time. Other visualizations might provide a different understanding of what is going on. This is a soap bubble animation of the main composite indices of six major countries, based on weekly values between 2000 and 2010 that I stole from Yahoo! Finance:

  • Dow Jones
  • CAC 40
  • Footsie 100
  • DAX
  • NIKKEI 225
  • SSE

The radius of the bubble represents the value of the index: the scales have been arbitrarily chosen so that all the bubbles have the same size in January 2000.

Browser requirements: IE9, FF3.5, Chrome 5, Safari 5

Viewing the data as an animation instead of a plot really helps underline the meanings of «sudden» and «simultaneous» that would otherwise be lost with a static representation, especially around August 2002 and August 2008. The huge, screen-blotting Chinese bubble is quite impressive, too. In the end, most bubbles are as large as they were in the beginning, if not smaller.

For historical reference, these are the plots for the six indices:

indices

What are your opinions on this animation? Does it work on your browser? Are there any similar data visualizations you would like to see here? Is there a point in explaining the bloody technical details behind rendering this baby?

Noticed the [type Function] bug in the bottom left corner of the Yahoo Finance image? ;)

Harbringer of Spring postmortem

Yesterday was the seventh installment of the Three Hour Game Development Contest, hosted on GameDev.Net by capn_midnight. The premise is that a theme is provided and contestants have three hours to design, implement and deliver a complete video game using art, libraries and technology of their choice, that is somehow related to that theme. The contest results will be revealed today.

harbringer-of-spring

My entry is called Harbringer of Spring, a game where you have to step on dirt tiles to make grass grow there, and step on the stone tile when there’s grass everywhere. To increase difficulty, water tiles cannot be traversed, and bridge tiles turn into water tiles once they are stepped over. It’s written in HTML-CSS-jQuery, sou can play it online. The original intent is vaguely inspired from the Bridges of Königsberg mathematical problem where you have to cross all bridges exactly once.

The technical details:

  • 233 lines of JavaScript code, divided into 100 lines of level data and 133 lines of actual gameplay code.
  • 53 lines of HTML
  • 174 lines of CSS, divided into 103 lines for the general UI (title, links) and 71 lines for the map and tileset.
  • Uses jQuery 1.4.2 to interact with the document.
  • Uses the PlanetCute tileset, because I couldn’t be bothered with graphics.

Here’s the step-by-step rundown of how I developed this entry (the given time is actually a countdown to the time the game must be delivered).

3:15 (that’s 15 minutes before the contest begins): I’ve already decided that the game shall be written in HTML-CSS-jQuery, so I set up my work environment accordingly. Desktop 1 will be my development environment, with a browser on the left monitor containing documentation for jQuery and an xterm on the right monitor connected to the nicollet.net server through SSH, running an emacs with two vertically split buffers. Desktop 2 will be my testing environment, with a browser containing the game on the left monitor and a maximized Firebug on the right monitor.

I also create the index.html, style.css and game.js files, download (with wget) the latest minified version of jQuery, and create an img directory for my images. I’m a bit bored, so I decide to add the “© 2010 Victor Nicollet” footer to the document.

3:00 : apparently, the theme has already been given out a little earlier. Time to start brainstorming. A few ideas I get right off the bat: a game like Sim City where you have to build spring factories in a world covered by winter. The Silk icon set from FamFamFam contains some weather icons, but they’re really small. Perhaps I could instead build a Risk-like game by placing absolutely positioned stacks of units on a world map image? I start browsing the web for acceptable tile sets, just in case.

2:45 : I stumble upon the PlanetCute tileset (courtesy of the GameDev.Net thread on art resources). The concept seems simple enough to implement a 2D tile map quickly, and I like the grass/dirt contrast. I download (with wget) the file and unzip it into img. The water tiles are quite cute, so I’m starting to think about “Spring” in terms of “Hot Spring” or something. Anyway, I need to be drawing tiles, I might as well get that out of the way while letting my right brain think about the theme.

2:30 : through a clever combination of float:left, position:absolute and background-image CSS trickery (with some help from Firebug for empirically guessing the pixel offsets I need) I manage to render the tile map, with the possibility of a “selection” golden light overlayed on top. It’s all controlled by classes on the root tile elements, so controlling it from jQuery will be easy.

I do have a problem, however: even though the root tile elements match the flat area on top of the tile graphics, the actual tile graphics are taller, and the CSS hover property seems to detect hovering even though I’m not above the root tile per se. I have no idea about how to resolve this quickly, so I’ll give up on selecting tiles with the mouse (there are plenty of things you can do with keyboard controls, and given the time I have at my disposal, trying to fix the issue might take much too long.

In the mean time, I thought about a simple “step over tiles to paint them” gameplay similar to Q*bert. The trouble is that the existing graphical framework does not support animations, so adding enemies to the fray might be a bad idea. Yet, “paint the nodes” reminds me of graph theory, which in turn reminds me of the Bridges of Königsberg problem. I quickly decide to add non-traversable areas (I did like those water tiles) with bridges that may only be crossed once. Being able to only use a resource once means having to decide when to use it and what for, and these kinds of decisions provide a nice puzzle game design. So be it. Time to implement!

2:00 : the first half hour is the hardest, because I have to set up the general layout of the game code before I can see anything move on the screen. I go for a simple architecture in jQuery-powered JavaScript : an initialization function sets up the DOM and places references to individual DOM tiles in a 2D array. Everything related to the game will be a member of the global «g» object. The code looks like this so far:

var $world   = $('#lyt-world');
g.$t = [];
for (var y = 0; y < 6; ++y ) {
  var r = [];
  for (var x = 0; x < 9; ++x) {
    r.push($('<div class="tile"><div class="tile-img"/><div class="tile-sel"/></div>').appendTo($world));
  }
  g.$t.push(r);
}

If you’ve already done tile-based rendering, you will probably recognize the YX way of doing things: tile x,y can be found at g.$t[y][x]. I’ve also decided that a level array named «l» will hold the levels, in an array-of-strings fashion (space is dirt, tilde is water, plus is bridge and hash is stone). This is what the first level looks like:

{
  name : '1. The beginning...',
  tile : [
    "   ~~~~~ ",
    "    ~#+  ",
    "~~~+~~~  ",
    "~   ~~~ ~",
    "~~+~~~~+~",
    "~        "
  ],
  init : [1,1]
}

Another major architectural decision was how to represent tiles that had been walked on. One possibility was to implement “on step” rules which altered the map whenever the player moved (but that would involve making a copy of the map present in the level definition). The other possibility was to store the list of all positions that had been stepped on in a way that’s easy to query for “has the player stepped there yet?”. JavaScript has dictionaries so I went with the latter, so the rendering code looked like this:

render : function() {
  for (var y = 0; y < 6; ++y) {
    for (var x = 0; x < 9; ++x) {
      var t = this.map[y].charAt(x),
         $t = g.$t[y][x];

      var xy = [x,y].join();

      $t.attr('class',[
        'tile',
        [x,y].join() == this.pos.join() ? 'show' : '',
        t == ' ' && xy in this.stepped ? 'grass' : '',
        t == ' ' && !(xy in this.stepped) ? 'dirt' : '',
        t == '~' || t == '+' && xy in this.stepped ? 'water' : '',
        t == '+' && !(xy in this.stepped) ? 'wood' : ''
      ].join(' '));
    }
  }
},

This made the “step onto tile” code exceedingly simple:

step : function(xy) {
 this.stepped[xy.join()] = true;
 this.pos = xy;
},

Using Firebug, it was fairly easy to test this by manually calling the step and rendering functions.

1:45 : implementing controls meant, mostly, catching keydown events on the document, detecting arrow key presses, and calling a “try step” function with the appropriate movement vector (that function would check if the movement was possible, then call the actual step function, and finally rendering the entire thing). What took me the longest was finding out what the key values were (I eventually found out by logging the key values to the Firebug console).

The game is now officially playable. I pour myself some hot Earl Grey Tea, eat a warm pancake, and give the first level a few test runs.

1:40 : it was pretty obvious that refreshing the entire page to restart the level was not very nice. In the name of polish, I add a “try again” button.

1:35 : turning a bridge into water as soon as it is stepped on feels weird. I split the “stepped” dictionary into “steppedOn” and “steppedOff” dictionaries that are filled independently, and adjust the rendering code accordingly.

1:30 : the game is quite simple. Maybe I could make it more complex if I forced the player to finish on a specific tile? I add stone tiles to the mix.

1:25 : I implement a “all tiles painted green” function and add a test in the “step” function to check the win condition (all tiles painted green and sitting on a stone tile). Winning starts the next level. The game is now officially in Alpha! Knowing that I can now deliver this simple game as is, I decide to move on to level design and design as many levels as possible.

Also, my fiancée just fell asleep. I guess I’ll plug in my headphones before I resume listening to Rammstein.

1:15 : after a bit of testing, it appears necessary to add a level selector, if only for level design purposes (I don’t want to have to solve seven levels just to test the eight). I now have two playable levels. I guess I should start designing more: aside from the game finished screen, everything’s implemented. In fact, there’s no instructions manual or tutorial yet, and the “step on the stone tile when you’re done” idea is not really that obvious.

1:00 : I write a few short text bubble to explain the game logic, place them in a layer that is only visible on the first level, and position them in Firebug. Now, the level design can start!

0:55 : level 3 (Chess) is over. It’s pretty easy to solve.

0:50 : I search for the actual Bridges of Königsberg on Wikipedia, and implement it as a (rather simple) level 4.

0:40 : I reuse the chessboard from  level 3, add another stone tile to confuse players, solve it while stepping on as few bridges as possible, then turn the unused bridges into water tiles. Seems obvious to me, but apparently this ended up being the most difficult level in the game.

0:30 : Out of ideas. I’ll try a random layout and work from there. This ends up as level 6 (Detour).

0:25 : Still out of ideas. Let’s draw a happy face: level 7 (Happy).

0:15 : Still out of ideas. I randomly draw something up, test it, and that’s it. I would have liked to draw more levels, but there was some polishing left to do (namely, the game finished screen).

0:10 : Game finished screen is over. Let’s start packaging the stuff.

0:05 : Last minute bug fix: apparently the overflow:hidden on the tile map behaved badly when the try again and select level elements were floated to the right and were past a certain height (on certain browsers). I add a clear:both to the tile map.

0:00 : as expected, I don’t have zip on my server, my aptitude cache is not up to date, and I don’t want to risk spending 10 minutes updating it. I should have fixed that before the contest! Still. I tar-gzip the stuff, download it on a Windows computer, extract it then turn it into a zip file, and upload it back to the server. There’s no time hunting for capn_midnight’s mail address, so I just post the links in the thread.

-0:05 : I manage to locate the captain’s address, so I mail the file there.

-0:10 : ?????

-0:15 : Profit.

http://en.wikipedia.org/wiki/Q*bert

How to build a page client-side?

The basic philosophy of jQuery is to start with some existing HTML sent over vanilla HTTP by the server. That HTML should be all you need (so that people without a JavaScript-enabled browser can still use the web site). Then, jQuery enhances that HTML by adding new behavior (usually changing the properties of existing elements, sometimes adding new elements).

This is very useful for small pieces of behavior, but writing complete and complex components is hard for several reasons:

  • A partial view strategy is required on the server side to insert the appropriate HTML in the appropriate location (as opposed to leaving an empty hole and having the component generate its own HTML).
  • If the behavior of your component is complex, then there will be a lot of parsing going on. A typical example would be sorting a table by a “date” column—since the date format in itself cannot be parsed (culture-dependent and may contain “Yesterday”, “13 seconds ago” and similar shortcuts).
  • Sometimes, the server needs to add information that is not visible, but is needed by the JavaScript. The format for sending this data (attribute, hidden field…) is difficult to document and type-check.
  • Selecting precisely the right fields in a blob of HTML, without hitting any others, is hard, especially for components that may later contain sub-components. Class-based selection is slow, id-based selection involves heavy logistics to move the identifiers around, and complete traversal takes a while and breaks if the HTML changes.

My preferred approach to JavaScript components is to receive JSON-formatted data from the server (easy to parse) from which I construct the DOM elements I need and capture them at the same time.

var $comment = $('<div><img/><span/><div/></div>')
  .addClass("comment");

var obj =
{
  $self : $comment,

  $img  : $comment.children('img')
          .attr('src',data.imgUrl),

  $name : $comment.children('span')
          .text(data.authorName)
          .addClass('authorName'),

  $body : $comment.children('div')
};

$.each(data.text,function(k,t){
  $('<p/>').text(t).appendTo(obj.$body);
});

return obj;

The point is that you then have access, through the returned object, to all the relevant elements within the comment, so that you may target them with effects without any risky selector-based magic. Besides, if the HTML format of comments changes, you will only have to change the code above and nothing else.

And of course, using text() escapes any dangerous HTML you might have.

To make the above appear in your code, all you have to do is:

var $commentsList = $('#my-comments-list');

$.each (comments, function(i,c){
  var obj = $comments[i] = renderComment(c);
  obj.$self.appendTo($commentsList);
});

This is usually where you hit a performance wall, because this is one of the slowest ways of using jQuery on a web page.

I’ve been in this situation recently on a smallish website that basically displays a list of contacts invited to various events as a 10-column/300-row table that includes additional functionality such as:

  • Dynamically add or remove new rows (with server-side confirms)
  • Rows are grouped together, and groups can be collapsed and expanded
  • Clicking on rows opens a modal editor, modifications are propagated back to the table
  • The data and formatting for certain rows depend on some other rows

The initial approach was exactly as described above: every cell was constructed as $('<td/>'), classes and attributes were applied to it, then all cells were inserted into rows constructed as $('<tr/>'), and these in turn were appended to the table tbody. Since some parts of the table were clickable to achieve various effects, jQuery’s click() function was used to add the appropriate event handlers, and the event handlers were closures that contained all relevant information about what row had to be collapsed or what element had to be removed.

The average time for rendering all of this was a solid 2200ms on Firefox 3.5, which felt about as dynamic as a dead tortoise nailed to a slab of concrete. For comparison purposes, rendering the data server-side and sending it to the client took about 390ms on average (arguably, the server would have scaling issues as it would have to render the HTML for all clients, but still).

2200ms means about 7ms per row. The problem here isn’t that the jQuery code is slow, but rather that it’s executed so many times to add up to a pretty large number.

My first attempt to improve performance was to avoid constructing rows cell by cell, instead building the final HTML of the row in one shot and then selecting clickable elements inside the row through their class to apply event handlers. Rows were then inserted into the table body using jQuery’s DOM functions. The new rendering time was 1800ms, which was not as good as I hoped my improvement to be.

The second step was to move away from selecting clickable elements to apply event handlers. This meant that I could either insert the event handler code in the HTML (but this meant no closures, so I would have to rely on global, non-garbage-collected behavior) or add a click event to the entire table and determine what element had been clicked (and parsing the DOM for information about what to do with the click, which was annoying).

I went with the first way, rewriting my code as global handlers and eliminating all the select-child-with-class overhead. Rows were still constructed independently and inserted independently. The improvement was sensible, as the rendering time was then 980ms.

The last wave of optimizations consisted in making sure the HTML for the entire table body was generated in one shot and concatenated as an array (using [a,b,c].join('') instead of a+b+c). This creates 5223-element array, concatenated into a string containing 72357 characters, which is then inserted into the table body using jQuery’s html() function. The entire process, including preliminary processing of the data to be displayed, takes about 160m (a 13.7× performance increase).

The change was mostly moving from this design pattern:

function renderRow(data)
{
  $tr = $('<tr/>');

  $('<td/>')
    .addClass('name')
    .append($('<a/>')
      .text(data.name)
      .click(function(){ frobnicate(data.id); }))
    .appendTo($tr);

  // ...

  return $tr;
}

To this one:

function renderRow(data,html)
{
  html.push(
    '<tr><td>',
    '<a href="javascript:frobnicate(',
    data.id,
    ')">',
    esc(data.name),
    '</a></td>',
    // ...
    '</tr>'
  );
}

Again, this is an extreme situation where page-generation goes way out of hand because a lot of rows are generate—the net benefit, as far as rendering a single row is concerned, is around 6ms. If your page contains only a small number of complex components, you can ignore the performance issues to get the components done, and only optimize if it turns out to be noticeable.

That DOM removal thing, again

Earlier this month, I pondered what looked like a bug in JavaScript/DOM/jQuery: removing an element from the DOM with jQuery (either manually with remove() or by setting the html() of its parent to something else) kept most of the data bound to the element around, but removed all event handlers from it. You could then re-insert the element, but its event handlers would be lost.

I then gathered from several sources, such as Stack Overflow, that this is a jQuery issue (or rather, feature) and not a JavaScript one.

The underlying cause is explained by Douglas Crockford:

When a DOM object contains a reference to a JavaScript object (such an event handling function), and when that JavaScript object contains a reference to that DOM object, then a cyclic structure is formed. This is not in itself a problem. At such time as there are no other references to the DOM object and the event handler, then the garbage collector (an automatic memory resource manager) will reclaim them both, allowing their space to be reallocated. The JavaScript garbage collector understands about cycles and is not confused by them. Unfortunately, IE’s DOM is not managed by JScript. It has its own memory manager that does not understand about cycles and so gets very confused. As a result, when cycles occur, memory reclamation does not occur.

A common solution to this problem is to remove the cycles when the element is removed from the DOM. Since a major source of cycles in your average jQuery program is the presence of event handlers, then removing the event handlers when an element is removed from the DOM solves the problem most of the time.

With the release of jQuery 1.4, the new documentation for .remove() makes mention of this fact:

In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed.

The documentation for .html() still makes no mention of this. If you want to remove an element and keep all the goodies you bound to it, jQuery 1.4 provides you with .detach():

The .detach() method is the same as .remove(), except that .detach() keeps all jQuery data associated with the removed elements. This method is useful when removed elements are to be reinserted into the DOM at a later time.

DOM removal and events

Let’s try something… go to a page with jQuery enabled (such as this one), and run the following code in your Javascript debugger console (such as Firebug):

var button =
  $('<button>Click me</button>')
  .click(function(){alert('Clicked!')})
  .appendTo('body')

In case you were wondering, this creates a brand new button, causes it to display a “Clicked!” message box when it’s clicked, and appends it to the document you are viewing.

Click on the button that just appeared : the message box appears. Not very surprising.

Now, run the following code on the same page :

$('body').html('');
button.appendTo('body')

As expected, everything on the page, including the button, disappears. However, the button is still referenced by the button variable, so it sticks around and we can append it back to the document. And indeed, it does appear on the page.

Click on the button again. This time, no message box appears.

I honestly have no idea why.

Gremlin : jQuery Growl

I have uploaded Gremlin, a simple jQuery-based Growl system, for elegant page-wide notification needs. Check it out, it’s free and built to be simple.

JavaScript Component Tutorial

Earlier this year, I ranted about how the graceful degradation model of jQuery made it hard to create complex components. Also, while working with a team on JavaScript components, I had to review all my previous takes on JavaScript architecture in order to build conventions that an entire team can follow.

Namespacing

To avoid collisions with other libraries, I create an object that uses a name I own. Any namespace name strategy is possible here, from java-like netNicolletCheese (if you have a common project name) to just cheese (if you have a project name that’s fairly unique). Then, any code I write goes into that namespace. I may further add sub-namespaces if I have a lot of code. Either way, you have to make sure the namespace exists before adding things to it, thus I add at the top of every file:

if(!('netNicolletCheese' in this)) this.netNicolletCheese = {};

The basic idea is that since I don’t know what order my files will be defined in, I have to define the namespace in every single one, while avoiding redefinition. This way, I can include files on an if-needed basis or stick them all together and remove any occurences of the namespace line except the first.

Then, everything is defined as members of that object. Executing any kind of code in library files is forbidden, only function and object definitions are allowed.

Components

A component is a class that contains data and renders itself somewhere on the page. This is different from the jQuery model of graceful degradation that assumes the rendered data is already present on the page and merely changes its layout. Use with caution, since this loses many benefits of graceful degradation like accessibility or search engine friendliness.

A component is always created as follows:

instance = new namespace.component(selector, data, options);
  • It’s always assigned to a variable. It’s a global variable if it’s defined at global scope (obviously, this may only happen in the code on a page, not in library code), and a public member variable of another object if it’s defined within an object. There are no free-floating components, every single one must be accessible from global scope as this makes command-line debugging way easier, and keeps the structure easier to see.
  • It has a first argument, which is a selector (in the jQuery sense). It will be fed to $(…) in order to get the target elements of the component (usually a single one). The typical behavior of a component is to generate some HTML from its internal state and call $(selector).html(…) to display the HTML. The selector is evaluated when the constructor is called, which means you may have to wrap the object initialization in a $(document).ready(…) to wait for the DOM to be instantiated. It also means adding any elements matching the selector later on won’t have any effect on the component.
  • It has a second argument, which is the data used to initialized the component. For instance, if the component is intended to display a list of elements, the data argument would be that list en JSON notation. This makes it easy to generate that data on the server side using one of the many JSON generators, while also making the component easy to instance on the client side programmatically.
  • It has an optional third argument, which represents the options that one may provide the component with (such as width, height, speed, effects, and so on). If it’s not part of the main data argument, it’s part of the options. The options are a classic JS record.

Component Initialization

The component is instantiated either when the document is ready, by placing the initialization code in the appropriate event, such as :

var page = {};
$(function(){ page.instance = new namespace.component(selector, data, options) });

Or it can be instantiated inside another component an an appropriate time.

The constructor itself consists of two distinct operations :

  • Set up any member variables representing the internal object state, using the data argument and options argument.
  • Render the object so that it appears on the page, using the rendering function, and passing the selector to it:
    this.render(selector);

    Note that a component may be created without a target selector, simply by using an empty array as the selector. It will remain unrendered until its render function is manually called with a valid selector as its argument.

Component Rendering

The render function is called during initialization. It’s also called whenever the entire component needs to be redrawn. Some components are small, and are redrawn every time, while other components may choose to only redraw parts of their contents and may therefore use other rendering functions for those parts. The rendering function reliably performs up to six operations:

  • It initializes the target, if it was provided. This lets the calling code change the rendering target dynamically.
    if (typeof(selector) != "undefined") this.$target = $(selector);

    This is generally useful when a component contains other components : a full rendering of the container means the target DOM elements of the inner components have been destroyed and created anew, and the container must therefore notify the inner components that they have a new target to render to.

    Note that the name of the target is always the same: for any component, component.$target is the current target of the component.

  • It optionally determines whether there is a target to begin with, to avoid unnecessary work. This usually takes the form :
    if (this.$target.get().length == 0) return;

    In the case where a component is inside a container, the container will create the component before rendering itself (to make things simpler, rendering assumes all sub-components already exist), and therefore provide an empty array as the selector.

  • It generates the full HTML for the component as a string.
  • It inserts the HTML into the DOM, replacing anything that previously existed. This usually happens as:
    this.$target.html(theGeneratedHtml);
  • It changes the rendering target of any sub-components and tells them to render themselves, usually written by extracting the correct targets from its own target and reverting it to an array of DOM elements:
    this.subComponent.render(this.$target.find('.subComponent').get());
  • It sets up any relevant events on the generated DOM. For instance, if the generated HTML contains a button, the button’s click event may be set to an event handler:
    this.$target.find('button').click(this.onButtonClick)

Component Event Handlers

It would be easy to define the “on button click” event simply as follows:

namespace.component.prototype.onButtonClick = function()
{ this.data.frobnicate(); }

But that wouldn’t work with jQuery, since the events re-bind the ‘this’ variable on the event handler before calling it. Meaning ‘this’ would be, in this case, the button DOM element instead of our component. This is bad.

The solution is to create an anonymous function that forwards the call to the appropriate member function:

this.$target.find('button').click(function(){this.onButtonClick()})

Whoops. ‘this’ doesn’t follow lexical scoping, which means this code still has the same problem. However, this can be solved quite easily:

var self = this;
this.$target.find('button').click(function(){self.onButtonClick()})

A short example

We can write a short incrementer: a button with a number that increases every time the button is pressed.

// Create the namespace if it doesn't exist
if (!('netNicollet' in this)) this.netNicollet = {};

// The constructor for our component
netNicollet.counter = function(selector, initial)
{
  // Set up data members (only one)
  this.value = initial;

  // Render the component
  this.render(selector);
}

// The rendering function
netNicollet.counter.prototype.render = function(selector)
{
  // Change the target (if applicable)
  if (typeof selector != "undefined")
    this.$target = $(selector);

  // Early-out if no target
  if (this.$target.get().length == 0)
    return;

  // Generate the HTML
  var html = '<div>' +  this.value + '</div>'
    + '<button type="button">Increment</button>';

  // Insert the HTML into the DOM
  this.$target.html(html);

  // Set up the events
  var self = this;
  this.$target.find('button')
    .click(function(){self.increment()});
}

// The increment operation
netNicollet.counter.prototype.increment = function()
{
  // Change the state
  this.value++;

  // Update the graphics
  this.render();
}

// Call this once the document is ready.
var counter = new netNicollet.counter('body', 1337);

The jQuery Syndrome

jQuery is a JavaScript library that makes the portable manipulation of the DOM easier. It provides primitives for selecting DOM elements using a simple CSS-similar syntax (courtesy of the Sizzle selector library), and functions for applying effects to the selected elements.

A few typical examples:

// Show all paragraphs on the page, even hidden ones.
$('p').show();

// Select all cells with class "important" within table "results"
// and make their background red
$('table#results td.important').css({background:'red'});

// Wait until page has loaded, then find all spans with class "mail",
// replace any '[nospam]' in their contents by '@' and add 'mailto' links
$(function(){
  $('span.mail').each(function(){
    var mail = $(this).html().replace('[nospam]', '@');
    $(this).html('<a href="mailto:'+mail+'">'+mail+'</a>');
  });
});

This ability to add dynamic behavior to elements at will by a select-and-apply process is extremely useful. For the HTML writer, adding behavior becomes as easy as adding a class to an element (using the above code, all you need to turn a piece of text into a mail is to put a span class=”mail” around it, no additional JavaScript required).

Yet, this approach does not scale well. Attempting to approach any non-trivial problem with a select-and-apply architecture inevitably leads to a heap of annoying problems.

The jQuery paradigm is $(target).operation(configuration); which implies that the dynamic data is within the target. This is what happens, for instance, in the mail rewriting example above: the address to be rewritten appears in the DOM, not in the JavaScript. This is one of the reasons why jQuery is so concise: most of the data is not specified as part of JavaScript code.

The problem appears when the data structures you manipulate are complex, because accessing data within the DOM is orders of magnitude harder than manipulating JavaScript data even with jQuery (and is utterly unacceptable with raw JavaScript). As long as your data is just a piece of visible text, or  the DOM structure itself, you will be fine. But as soon as you need additional data and invisible fields, you will start storing that data in attributes (<a name=”your data here”></a>) and the overhead of accessing it will kill both your productivity and your page’s performance.

It also appears when the behavior involves several interacting pieces on the page. Independent manipulation of pieces poses no problem in the jQuery paradigm, but what happens if two parts of a script attempt concurrent manipulation of a single piece?

Consider a very simple example that contains an image with a “hide” button that hides the image, a “show” button that shows the image, and a “delete” button that deletes the image. We want the hide and show effects to involve some animation. No button should be pressed while an animation is playing, so it is necessary to have all buttons be disabled whenever an animation starts, and then enable only the acceptable buttons when the animation ends. This gets increasingly complex as time passes, unless you simply use a lock: set the lock variable to true when the animation starts, set the lock variable to false when the animation ends, and have all buttons do nothing if the lock variable is set to true.

What I call the jQuery syndrome is the naive approach to making a page dynamic by using the select-apply operation on every item that is meant to be dynamic, and noticing a few days later that the data is so hard to extract, or the interactions so hard to synchronize, that you will need to refactor everything into a more scalable architecture.

My preferred approach to jQuery-based architecture, as detailed in an upcoming JITBrain article, is to have a central JavaScript object that controls the data for what is being displayed (usually one for every independent piece of your page). This object has a print() function that constructs or updates the corresponding DOM elements on the page and binds their events to its own member functions.

So, a typical event (onCreate, onClick…) happens as follows:

function onShowButtonClick()
  update data
  this.print() 

function print()
  update DOM from data
  bind show button click to this.onShowButtonClick

Of course, in practice there will be a lot more binding going on, since every button has to be bound to an action. Then, depending on how much time you are ready to spend on the matter, you can implement your print function as a “delete everything, replace with new values” or as an incremental update of the DOM areas that do not match the internal data.

Calendrier de l’Avent

Je vous propose pour les fêtes de fin d’année un calendrier de l’Avent. Il s’agit d’un petit bout de HTML gratuit que vous pouvez utiliser à volonté sur vos pages. Il propose entre autres:

  • 25 petites images de chez n.design studio.
  • Une grille de 5×5 jours, répartis aléatoirement. Cliquer sur un jour affiche l’image pour ce jour.
  • On ne peut voir que les images du jour et des jours précédents.
  • Le script se souvient des images déjà ouvertes par un visiteur d’une visite à l’autre.
  • Les jours changent aléatoirement de place (en conservant leur image).

Pour utiliser ce script, il suffit d’ajouter ces trois lignes au code HTML des pages à l’endroit où on veut le faire apparaître:

<iframe width=150 height=172 frameborder=0 src=http://www.nicollet.net/avent></iframe>

Vous pouvez l’installer dans la charte graphique du site, si vous le souhaitez. Référez-vous à la documentation de votre site/blog pour savoir comment insérer du HTML (ou postez un commentaire ici et j’essaierai de vous aider). Exemple de calendrier en action:



1170 feed subscribers
(readers who polled a feed this week)