intertwingly

It’s just data

jQuery and Closures


I’ve taken a first look at jQuery, and while I’m generally averse to frameworks, this truly is a library, one that you can use as little or as much as you like, and doesn’t try to do too much.

Its obvious strengths are being CSS selector based and having a consistent approach to collections and chainability.

An example of not trying to do too much is how you enable/disable an element.  That could easily have been encapsulated, but jQuery’s approach seems to be to not sweat the small stuff.

Using jQuery, one quickly picks up a few idioms.  For example, replacing an element typically is done via inserting the replacement before (or after) the current one, and them removing the current one, all in a chain, thus: $("#foo").before(bar).remove().

The net effect of all of this is that you can “write less and do more”, as promised.  But something that more fundamentally changes the way one writes scripts seems to have gone under-reported, at least to my quick scan of the web.  I discovered this trying to make a small mod to an existing data entry application, involving four fields per entry.  This has been used for some time now to create new entries, but now I want to adapt it to handle the updating of existing legacy data which only had three fields.

The approach I selected was to define a button which, when pressed, would cause an “AJAX” request to be issued downloading all of the legacy entries.  Once received, the first input field would be replaced by a dropdown selection box.  Once a selection was made, the selection box would revert back to an input field, three fields would be filled in, a hidden field gets information on the original record to be replaced (in case there is significant editing of the entry) and focus placed on the original fourth and final field.  Nothing fancy, but we have to deal with the fact that everything is asynchronous.

The final code is below:

$("#archive").click(function() {
  $.getJSON('unscanned.cgi', {}, function(unscanned) {

    // replace realname input field with a selection list
    var select = $('<select name="realname" id="realname"/>')[0];
    for (var i=0; i<unscanned.length; i++) {
      select.options[i] = new Option(unscanned[i][1], i);
    }
    $('#realname').before(select).remove();

    $("#archive").attr("disabled","disabled");

    // process selection
    $('#realname').focus().change(function() {
      var icla = unscanned[$("#realname option:selected").val()];
      $("#realname").before('<input type="text" ' + 
        'id="realname" name="realname"/>').remove();
      $("#realname").val(icla[1]);
      $("#pubname").val(icla[2]);
      $("#email").val(icla[3]);
      $("#replaces").val(icla[0] + ':' + icla[3]);
      $("#filename").val('').focus();
      $("#archive").removeAttr("disabled");
    });
  });
  return false;
});

The notable thing about this is that despite all of the asynchronous events taking place, the code is sequential (nested, but sequential), and that the JSON results of the AJAX call are immediately available to the function that is invoked when the selection changes.

With this in place, all that is required is for the server to return a bit of JSON (and all popular languages these days have good libraries for doing so).